diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 45fc4e1e..e8f45364 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -17,40 +17,6 @@ env: jobs: - test_win: - runs-on: windows-latest - defaults: - run: - shell: msys2 {0} - - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: MSYS2 Setup - uses: msys2/setup-msys2@v2 - with: - msystem: MINGW64 - update: false - install: mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-python mingw-w64-x86_64-ninja mingw-w64-x86_64-lld - - - name: Configure CMake - working-directory: ${{github.workspace}} - run: cmake -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_CXX_COMPILER=g++ -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_FLAGS="-fuse-ld=lld" -DRUN_TESTS=ON - - - name: Build - working-directory: ${{github.workspace}} - run: cmake --build build --config ${{env.BUILD_TYPE}} -j 2 - - - name: Run Examples - working-directory: ${{github.workspace}}/build/examples - run: python3 run_all_examples.py - - - name: Run Tests - working-directory: ${{github.workspace}}/build - run: ctest -V - test_mac: runs-on: macos-latest diff --git a/docs/pages/getting_started/installation_building.md b/docs/pages/getting_started/installation_building.md index da020cb2..131988d4 100644 --- a/docs/pages/getting_started/installation_building.md +++ b/docs/pages/getting_started/installation_building.md @@ -33,10 +33,10 @@ We suggest using the most recent versions when possible. ```bash mkdir build && cd build -cmake -DCMAKE_BUILD_TYPE=Release -DUSE_CUDA=CUDA .. +cmake -DCMAKE_BUILD_TYPE=Release -DUSE_CUDA=OFF .. make ``` -Where `CUDA` is `ON` if CUDA support is needed and `OFF` otherwise. +This will build the library without CUDA support. If CUDA support is needed, then replace `-DUSE_CUDA=OFF` with `-DUSE_CUDA=ON`. ```{warning} If the library is installed with cuda support, the user code must be compiled using `nvcc`. diff --git a/src/class_instantiation_list.json b/src/class_instantiation_list.json index 79fd926f..17c85890 100644 --- a/src/class_instantiation_list.json +++ b/src/class_instantiation_list.json @@ -112,6 +112,13 @@ "folder": null, "exceptions": null }, + { + "template": "class HyperGraph<$id_type, $nnz_type, $value_type>", + "filename": "object.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, { "template": "class ConverterOrderOne<$value_type>", "filename": "converter_order_one.inc", @@ -224,6 +231,27 @@ "folder": null, "exceptions": null }, + { + "template": "class MetisGraphReader<$id_type, $nnz_type, $value_type>", + "filename": "metis_graph_reader.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, + { + "template": "class MetisGraphWriter<$id_type, $nnz_type, $value_type>", + "filename": "metis_graph_writer.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, + { + "template": "class MTXWriter<$id_type, $nnz_type, $value_type>", + "filename": "mtx_writer.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, { "template": "class PigoEdgeListReader<$id_type, $nnz_type, $value_type>", "filename": "pigo_edge_list_reader.inc", @@ -238,6 +266,20 @@ "folder": null, "exceptions": null }, + { + "template": "class PatohReader<$id_type, $nnz_type, $value_type>", + "filename": "patoh_reader.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, + { + "template": "class PatohWriter<$id_type, $nnz_type, $value_type>", + "filename": "patoh_writer.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, { "template": "class BinaryReaderOrderTwo<$id_type, $nnz_type, $value_type>", "filename": "binary_reader_order_two.inc", @@ -343,6 +385,13 @@ "folder": null, "exceptions": null }, + { + "template": "class Bandwidth<$id_type, $nnz_type, $value_type>", + "filename": "bandwidth.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, { "template": "class RCMReorder<$id_type, $nnz_type, $value_type>", "filename": "rcm_reorder.inc", @@ -364,6 +413,20 @@ "folder": null, "exceptions": null }, + { + "template": "class SlashburnReorder<$id_type, $nnz_type, $value_type>", + "filename": "slashburn_reorder.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, + { + "template": "class BOBAReorder<$id_type, $nnz_type, $value_type>", + "filename": "boba_reorder.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, { "template": "class GenericReorder<$id_type, $nnz_type, $value_type>", "filename": "generic_reorder.inc", @@ -469,6 +532,13 @@ "folder": null, "exceptions": null }, + { + "template": "class MinDegreeColumn<$id_type, $nnz_type, $value_type>", + "filename": "min_degree_column.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, { "template": "class MaxDegree<$id_type, $nnz_type, $value_type>", "filename": "max_degree.inc", @@ -476,6 +546,13 @@ "folder": null, "exceptions": null }, + { + "template": "class MaxDegreeColumn<$id_type, $nnz_type, $value_type>", + "filename": "max_degree_column.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, { "template": "class AvgDegree<$id_type, $nnz_type, $value_type, $float_type>", "filename": "avg_degree.inc", @@ -483,6 +560,34 @@ "folder": null, "exceptions": null }, + { + "template": "class AvgDegreeColumn<$id_type, $nnz_type, $value_type, $float_type>", + "filename": "avg_degree_column.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, + { + "template": "class StandardDeviationDegreeColumn<$id_type, $nnz_type, $value_type, $float_type>", + "filename": "standard_deviation_degree_column.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, + { + "template": "class CoefficientOfVariationDegreeColumn<$id_type, $nnz_type, $value_type, $float_type>", + "filename": "coefficient_of_variation_degree_column.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, + { + "template": "class GeometricAvgDegreeColumn<$id_type, $nnz_type, $value_type, $float_type>", + "filename": "geometric_avg_degree_column.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, { "template": "class MinMaxAvgDegree<$id_type, $nnz_type, $value_type, $float_type>", "filename": "min_max_avg_degree.inc", @@ -490,6 +595,34 @@ "folder": null, "exceptions": null }, + { + "template": "class MedianDegreeColumn<$id_type, $nnz_type, $value_type, $float_type>", + "filename": "median_degree_column.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, + { + "template": "class OffDiagBlockNNZ<$id_type, $nnz_type, $value_type>", + "filename": "off_diag_block_nnz.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, + { + "template": "class Profile<$id_type, $nnz_type, $value_type>", + "filename": "profile.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, + { + "template": "class TriangleCount<$id_type, $nnz_type, $value_type>", + "filename": "triangle_count.inc", + "ifdef": null, + "folder": null, + "exceptions": null + }, { "template": "class JaccardWeights<$id_type, $nnz_type, $value_type, $float_type>", "filename": "jaccard_weights.inc", diff --git a/src/sparsebase/bases/iobase.h b/src/sparsebase/bases/iobase.h index 42235fff..ee0a11e5 100644 --- a/src/sparsebase/bases/iobase.h +++ b/src/sparsebase/bases/iobase.h @@ -15,6 +15,7 @@ #include "sparsebase/io/binary_writer_order_two.h" #include "sparsebase/io/edge_list_reader.h" #include "sparsebase/io/mtx_reader.h" +#include "sparsebase/io/mtx_writer.h" #include "sparsebase/io/pigo_edge_list_reader.h" #include "sparsebase/io/pigo_mtx_reader.h" #include "sparsebase/io/reader.h" @@ -290,6 +291,102 @@ class IOBase { io::BinaryWriterOrderOne writer(filename); return writer.WriteArray(array); } + //! Write a CSR object to a matrix market file + /*! + * Write a CSR object to a matrix market file. + * @tparam IDType type to represent the number of rows and columns in the + * object. + * @tparam NNZType type to represent the number of non-zeros in the object. + * @tparam ValueType type to represent the data inside the matrix (vertex + * weights in the case of a graph). + * @param csr a pointer at the `CSR` object to write. + * @param filename path to write the file. + * @param object is either matrix or vector. + * @param format is either coordinate or array. + * @param field is either real, double, complex, integer or pattern. + * @param symmetry is either general (legal for real, complex, + integer or pattern fields), symmetric (real, complex, integer or pattern), + skew-symmetric (real, complex or integer), or hermitian (complex only). + */ + template + static void WriteCSRToMTX( + format::CSR* csr, + std::string filename, + std::string object = "matrix", + std::string format = "coordinate", + std::string field = "real", + std::string symmetry = "general") { + io::MTXWriter writer(filename, + object, + format, + field, + symmetry); + return writer.WriteCSR(csr); + } + //! Write a COO object to a matrix market file + /*! + * Write a COO object to a matrix market file. + * @tparam IDType type to represent the number of rows and columns in the + * object. + * @tparam NNZType type to represent the number of non-zeros in the object. + * @tparam ValueType type to represent the data inside the matrix (vertex + * weights in the case of a graph). + * @param coo a pointer at the `COO` object to write. + * @param filename path to write the file. + * @param object is either matrix or vector. + * @param format is either coordinate or array. + * @param field is either real, double, complex, integer or pattern. + * @param symmetry is either general (legal for real, complex, + integer or pattern fields), symmetric (real, complex, integer or pattern), + skew-symmetric (real, complex or integer), or hermitian (complex only). + */ + template + static void WriteCOOToMTX( + format::CSR* coo, + std::string filename, + std::string object = "matrix", + std::string format = "coordinate", + std::string field = "real", + std::string symmetry = "general") { + io::MTXWriter writer(filename, + object, + format, + field, + symmetry); + return writer.WriteCOO(coo); + } + //! Write an Array object to a matrix market file + /*! + * Write an Array object to a matrix market file. + * @tparam IDType type to represent the number of rows and columns in the + * object. + * @tparam NNZType type to represent the number of non-zeros in the object. + * @tparam ValueType type to represent the data inside the array (vertex + * weights in the case of a graph). + * @param arr a pointer at the `Array` object to write. + * @param filename path to write the file. + * @param object is either matrix or vector. + * @param format is either coordinate or array. + * @param field is either real, double, complex, integer or pattern. + * @param symmetry is either general (legal for real, complex, + integer or pattern fields), symmetric (real, complex, integer or pattern), + skew-symmetric (real, complex or integer), or hermitian (complex only). + */ + template + static void WriteArrayToMTX( + format::Array* arr, + std::string filename, + std::string object = "matrix", + std::string format = "coordinate", + std::string field = "real", + std::string symmetry = "general") { + io::MTXWriter writer(filename, + object, + format, + field, + symmetry); + return writer.WriteArray(arr); + } }; } // namespace sparsebase::bases #endif // SPARSEBASE_PROJECT_IOBASE_H diff --git a/src/sparsebase/feature/avg_degree_column.cc b/src/sparsebase/feature/avg_degree_column.cc new file mode 100644 index 00000000..5a9e06cc --- /dev/null +++ b/src/sparsebase/feature/avg_degree_column.cc @@ -0,0 +1,142 @@ +#include "sparsebase/feature/avg_degree_column.h" + +#include +#include +#include +#include +#include + +#include "sparsebase/utils/parameterizable.h" + +namespace sparsebase::feature { + +template +AvgDegreeColumn::AvgDegreeColumn() { + Register(); + this->params_ = + std::shared_ptr(new AvgDegreeColumnParams()); + this->pmap_.insert({get_id_static(), this->params_}); +} +template +AvgDegreeColumn::AvgDegreeColumn( + AvgDegreeColumnParams params) { + AvgDegreeColumn(); +} + +template +AvgDegreeColumn::AvgDegreeColumn( + const AvgDegreeColumn &d) { + Register(); + this->params_ = d.params_; + this->pmap_ = d.pmap_; +} + +template +AvgDegreeColumn::AvgDegreeColumn( + const std::shared_ptr p) { + Register(); + this->params_ = p; + this->pmap_[get_id_static()] = p; +} + +template +void AvgDegreeColumn::Register() { + this->RegisterFunction( + {format::CSC::get_id_static()}, + GetAvgDegreeColumnCSC); +} + +template +std::unordered_map +AvgDegreeColumn::Extract( + format::Format *format, std::vector c, + bool convert_input) { + return {{this->get_id(), std::forward( + GetAvgDegreeColumn(format, c, convert_input))}}; +}; + +template +std::vector +AvgDegreeColumn::get_sub_ids() { + return {typeid(AvgDegreeColumn)}; +} + +template +std::vector +AvgDegreeColumn::get_subs() { + return { + new AvgDegreeColumn(*this)}; +} + +template +std::type_index +AvgDegreeColumn::get_id_static() { + return typeid(AvgDegreeColumn); +} + +template +AvgDegreeColumn::~AvgDegreeColumn() = default; + +template +std::tuple>, FeatureType *> +AvgDegreeColumn:: + GetAvgDegreeColumnCached(format::Format *format, + std::vector contexts, + bool convert_input) { + AvgDegreeColumnParams params; + return this->CachedExecute(¶ms, contexts, convert_input, false, + format); // func(sfs, this->params_.get()); +} +template +FeatureType * +AvgDegreeColumn::GetAvgDegreeColumn( + format::Format *format, std::vector contexts, + bool convert_input) { + AvgDegreeColumnParams params; + return this->Execute(¶ms, contexts, convert_input, + format); // func(sfs, this->params_.get()); +} + +template +FeatureType * +AvgDegreeColumn::GetAvgDegreeColumn( + object::Graph *obj, + std::vector contexts, bool convert_input) { + format::Format *format = obj->get_connectivity(); + return this->Execute(this->params_.get(), contexts, convert_input, + format); // func(sfs, this->params_.get()); +} + +template +FeatureType *AvgDegreeColumn:: + GetAvgDegreeColumnCSC(std::vector formats, + utils::Parameters *params) { + auto csc = formats[0]->AsAbsolute>(); + IDType num_col = csc->get_dimensions()[0]; + FeatureType *avg_degree_column = new FeatureType; + auto *cols = csc->get_col_ptr(); + NNZType degree_sum = cols[num_col] - cols[0]; + *avg_degree_column = degree_sum / (FeatureType)num_col; + return avg_degree_column; +} + +#if !defined(_HEADER_ONLY) +#include "init/avg_degree_column.inc" +#endif +} // namespace sparsebase::feature diff --git a/src/sparsebase/feature/avg_degree_column.h b/src/sparsebase/feature/avg_degree_column.h new file mode 100644 index 00000000..df4102ac --- /dev/null +++ b/src/sparsebase/feature/avg_degree_column.h @@ -0,0 +1,105 @@ +#include + +#include "sparsebase/config.h" +#include "sparsebase/feature/feature_preprocess_type.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/object/object.h" +#include "sparsebase/utils/parameterizable.h" + +#ifndef SPARSEBASE_PROJECT_AVG_DEGREE_COLUMN_H +#define SPARSEBASE_PROJECT_AVG_DEGREE_COLUMN_H +namespace sparsebase::feature { + +//! An empty struct used for the parameters of AvgDegree +struct AvgDegreeColumnParams : utils::Parameters {}; +//! Find the average degree of the graph representation of a format object +/*! + * + * @tparam FeatureType the type in which the distribution value are returned -- + * should be a floating type + */ +template +class AvgDegreeColumn + : public feature::FeaturePreprocessType { + public: + //! An empty struct used for the parameters of AvgDegree + typedef AvgDegreeColumnParams ParamsType; + AvgDegreeColumn(); + AvgDegreeColumn(AvgDegreeColumnParams); + AvgDegreeColumn(const AvgDegreeColumn &); + AvgDegreeColumn(std::shared_ptr); + virtual std::unordered_map Extract( + format::Format *format, std::vector, + bool convert_input); + virtual std::vector get_sub_ids(); + virtual std::vector get_subs(); + static std::type_index get_id_static(); + + //! Average degree column generation executor function that carries out function + //! matching + /*! + * + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return average degree in the graph representation of `format` + */ + FeatureType *GetAvgDegreeColumn(format::Format *format, + std::vector contexts, + bool convert_input); + //! Average degree generation executor function that carries out function + //! matching on a Graph + /*! + * + * @param object a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return average degree in the graph + */ + FeatureType *GetAvgDegreeColumn( + object::Graph *object, + std::vector contexts, bool convert_input); + //! Average degree generation executor function that carries out function + //! matching with cached outputs + /*! + * Generates the average degree of the passed format. If the input format + * was converted to other format types, the converting results are also + * returned with the output @param format a single format pointer to any + * format @param contexts vector of contexts that can be used for extracting + * features. + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return A tuple with the first element being a vector of Format* + * where each pointer in the output points at the format that the corresponds + * Format object from the the input was converted to. If an input Format + * wasn't converted, the output pointer will point at nullptr. The second + * element is a pointer pointing to average degree + */ + std::tuple>, FeatureType *> + GetAvgDegreeColumnCached(format::Format *format, + std::vector contexts, + bool convert_input); + + static FeatureType + * + //! Average degree generation implementation function for CSRs + /*! + * + * @param format a single format pointer to any format + * @return average degree in the graph representation of `formats[0]` + */ + GetAvgDegreeColumnCSC(std::vector formats, + utils::Parameters *params); + ~AvgDegreeColumn(); + + protected: + void Register(); +}; +} // namespace sparsebase::feature +#ifdef _HEADER_ONLY +#include "sparsebase/feature/avg_degree_column.cc" +#endif +#endif // SPARSEBASE_PROJECT_AVG_DEGREE_COLUMN_H diff --git a/src/sparsebase/feature/bandwidth.cc b/src/sparsebase/feature/bandwidth.cc new file mode 100644 index 00000000..1fd3fc9f --- /dev/null +++ b/src/sparsebase/feature/bandwidth.cc @@ -0,0 +1,117 @@ +#include "sparsebase/feature/bandwidth.h" + +#include +#include +#include +#include +#include + +#include "sparsebase/utils/parameterizable.h" + +namespace sparsebase::feature { + +template +Bandwidth::Bandwidth(ParamsType) { + Bandwidth(); +} +template +Bandwidth::Bandwidth() { + Register(); + this->params_ = std::shared_ptr(new ParamsType()); + this->pmap_.insert({get_id_static(), this->params_}); +} + +template +Bandwidth::Bandwidth( + const Bandwidth &d) { + Register(); + this->params_ = d.params_; + this->pmap_ = d.pmap_; +} + +template +Bandwidth::Bandwidth( + const std::shared_ptr r) { + Register(); + this->params_ = r; + this->pmap_[get_id_static()] = r; +} + +template +Bandwidth::~Bandwidth() = default; + +template +void Bandwidth::Register() { + this->RegisterFunction( + {format::CSR::get_id_static()}, + GetBandwidthCSR); +} + +template +std::vector +Bandwidth::get_sub_ids() { + return {typeid(Bandwidth)}; +} + +template +std::vector +Bandwidth::get_subs() { + return {new Bandwidth(*this)}; +} + +template +std::type_index Bandwidth::get_id_static() { + return typeid(Bandwidth); +} + +template +std::unordered_map +Bandwidth::Extract(format::Format *format, + std::vector c, + bool convert_input) { + return {{this->get_id(), + std::forward(GetBandwidth(format, c, convert_input))}}; +}; + +template +int *Bandwidth::GetBandwidth( + format::Format *format, std::vector c, + bool convert_input) { + return this->Execute(this->params_.get(), c, convert_input, format); +} + +template +std::tuple>, int *> +Bandwidth::GetBandwidthCached( + format::Format *format, std::vector c, + bool convert_input) { + return this->CachedExecute(this->params_.get(), c, convert_input, false, + format); +} + +template +int *Bandwidth::GetBandwidthCSR( + std::vector formats, utils::Parameters *params) { + auto csr = formats[0]->AsAbsolute>(); + auto dims = csr->get_dimensions(); + IDType num_rows = dims[0]; //?? + NNZType *rows = csr->get_row_ptr(); + IDType *columns = csr->get_col(); + int bandwidth = 0; + for (NNZType i = 0; i < num_rows; i++) { + for (NNZType k = rows[i]; k < rows[i + 1]; k++) { + IDType j = columns[k]; + if (i >= j && bandwidth < i - j + 1) + bandwidth = i - j + 1; + else if (i < j && bandwidth < j - i + 1) + bandwidth = j - i + 1; + } + } + + return new int(bandwidth); +} + +#if !defined(_HEADER_ONLY) +#include "init/bandwidth.inc" +#endif +} // namespace sparsebase::feature diff --git a/src/sparsebase/feature/bandwidth.h b/src/sparsebase/feature/bandwidth.h new file mode 100644 index 00000000..fb316f41 --- /dev/null +++ b/src/sparsebase/feature/bandwidth.h @@ -0,0 +1,76 @@ +#include + +#include "sparsebase/config.h" +#include "sparsebase/feature/feature_preprocess_type.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/utils/parameterizable.h" + +#ifndef SPARSEBASE_PROJECT_BANDWIDTH_H +#define SPARSEBASE_PROJECT_BANDWIDTH_H +namespace sparsebase::feature { +//! Bandwidth calculation class for graph representation +template +class Bandwidth : public feature::FeaturePreprocessType { + public: + //! An empty struct used for the parameters of Bandwidth + typedef utils::Parameters ParamsType; + Bandwidth(); + Bandwidth(ParamsType); + Bandwidth(const Bandwidth &d); + Bandwidth(std::shared_ptr); + std::unordered_map Extract( + format::Format *format, std::vector, + bool convert_input) override; + std::vector get_sub_ids() override; + std::vector get_subs() override; + static std::type_index get_id_static(); + + //! Calculate the bandwidth of the format + /*! + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * features. + * @param convert_input whether or not to convert the input format if that + * is needed. + * \return calculated bandwidth of graph representation of `format` + */ + int *GetBandwidth(format::Format *format, + std::vector contexts, + bool convert_input); + std:: + tuple>, int *> + //! Calculate the bandwidth of the format with cached output + /*! + * + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * features. + * @param convert_input whether or not to convert the input format if that + * is needed. + * @return calculated bandwidth of graph representation of `format` + */ + GetBandwidthCached(format::Format *format, + std::vector contexts, + bool convert_input); + //! Bandwidth implementation function for CSRs + /*! + * + * @param formats A vector containing a single format pointer that should + * point at a CSR object + * @param params a Parameters pointer, though it + * is not used in the function + * @return calculated bandwidth of graph representation of `format[0]` + */ + static int *GetBandwidthCSR(std::vector formats, + utils::Parameters *params); + ~Bandwidth(); + + protected: + void Register(); +}; + +} // namespace sparsebase::feature +#ifdef _HEADER_ONLY +#include "sparsebase/feature/bandwidth.cc" +#endif +#endif // SPARSEBASE_PROJECT_BANDWIDTH_H \ No newline at end of file diff --git a/src/sparsebase/feature/coefficient_of_variation_degree_column.cc b/src/sparsebase/feature/coefficient_of_variation_degree_column.cc new file mode 100644 index 00000000..eb3bcf21 --- /dev/null +++ b/src/sparsebase/feature/coefficient_of_variation_degree_column.cc @@ -0,0 +1,147 @@ +#include "coefficient_of_variation_degree_column.h" + +#include +#include +#include +#include +#include +#include + +#include "sparsebase/utils/parameterizable.h" + +namespace sparsebase::feature { + +template +CoefficientOfVariationDegreeColumn::CoefficientOfVariationDegreeColumn() { + Register(); + this->params_ = + std::shared_ptr(new CoefficientOfVariationDegreeColumnParams()); + this->pmap_.insert({get_id_static(), this->params_}); +} +template +CoefficientOfVariationDegreeColumn::CoefficientOfVariationDegreeColumn( + CoefficientOfVariationDegreeColumnParams params) { + CoefficientOfVariationDegreeColumn(); +} + +template +CoefficientOfVariationDegreeColumn::CoefficientOfVariationDegreeColumn( + const CoefficientOfVariationDegreeColumn &d) { + Register(); + this->params_ = d.params_; + this->pmap_ = d.pmap_; +} + +template +CoefficientOfVariationDegreeColumn::CoefficientOfVariationDegreeColumn( + const std::shared_ptr p) { + Register(); + this->params_ = p; + this->pmap_[get_id_static()] = p; +} + +template +void CoefficientOfVariationDegreeColumn::Register() { + this->RegisterFunction( + {format::CSC::get_id_static()}, + GetCoefficientOfVariationDegreeColumnCSC); +} + +template +std::unordered_map +CoefficientOfVariationDegreeColumn::Extract( + format::Format *format, std::vector c, + bool convert_input) { + return {{this->get_id(), std::forward( + GetCoefficientOfVariationDegreeColumn(format, c, convert_input))}}; +}; + +template +std::vector +CoefficientOfVariationDegreeColumn::get_sub_ids() { + return {typeid(CoefficientOfVariationDegreeColumn)}; +} + +template +std::vector +CoefficientOfVariationDegreeColumn::get_subs() { + return { + new CoefficientOfVariationDegreeColumn(*this)}; +} + +template +std::type_index +CoefficientOfVariationDegreeColumn::get_id_static() { + return typeid(CoefficientOfVariationDegreeColumn); +} + +template +CoefficientOfVariationDegreeColumn::~CoefficientOfVariationDegreeColumn() = default; + +template +std::tuple>, FeatureType *> +CoefficientOfVariationDegreeColumn:: + GetCoefficientOfVariationDegreeColumnCached(format::Format *format, + std::vector contexts, + bool convert_input) { + CoefficientOfVariationDegreeColumnParams params; + return this->CachedExecute(¶ms, contexts, convert_input, false, + format); // func(sfs, this->params_.get()); +} +template +FeatureType * +CoefficientOfVariationDegreeColumn::GetCoefficientOfVariationDegreeColumn( + format::Format *format, std::vector contexts, + bool convert_input) { + CoefficientOfVariationDegreeColumnParams params; + return this->Execute(¶ms, contexts, convert_input, + format); // func(sfs, this->params_.get()); +} + +template +FeatureType * +CoefficientOfVariationDegreeColumn::GetCoefficientOfVariationDegreeColumn( + object::Graph *obj, + std::vector contexts, bool convert_input) { + format::Format *format = obj->get_connectivity(); + return this->Execute(this->params_.get(), contexts, convert_input, + format); // func(sfs, this->params_.get()); +} + +template +FeatureType *CoefficientOfVariationDegreeColumn:: + GetCoefficientOfVariationDegreeColumnCSC(std::vector formats, + utils::Parameters *params) { + auto CSC = formats[0]->AsAbsolute>(); + IDType num_cols = CSC->get_dimensions()[0]; + FeatureType avg_degree; + auto *cols = CSC->get_col_ptr(); + NNZType degree_sum = cols[num_cols] - cols[0]; + avg_degree = degree_sum / (FeatureType)num_cols; + FeatureType standard_deviation_degree = 0; + for (int i = 0; i < num_cols; i++) { + standard_deviation_degree += (cols[i + 1] - cols[i] - avg_degree)*(cols[i + 1] - cols[i] - avg_degree); + } + return new FeatureType(sqrt(standard_deviation_degree)/avg_degree); +} + +#if !defined(_HEADER_ONLY) +#include "init/coefficient_of_variation_degree_column.inc" +#endif +} // namespace sparsebase::feature \ No newline at end of file diff --git a/src/sparsebase/feature/coefficient_of_variation_degree_column.h b/src/sparsebase/feature/coefficient_of_variation_degree_column.h new file mode 100644 index 00000000..fddf528b --- /dev/null +++ b/src/sparsebase/feature/coefficient_of_variation_degree_column.h @@ -0,0 +1,107 @@ +#include + +#include "sparsebase/config.h" +#include "sparsebase/feature/feature_preprocess_type.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/object/object.h" +#include "sparsebase/utils/parameterizable.h" + +#ifndef SPARSEBASE_PROJECT_COEFFICIENT_OF_VARIATION_DEGREE_COLUMN_H +#define SPARSEBASE_PROJECT_COEFFICIENT_OF_VARIATION_DEGREE_COLUMN_H + +namespace sparsebase::feature { + +//! An empty struct used for the parameters of CoefficientOfVariationDegreeColumn +struct CoefficientOfVariationDegreeColumnParams : utils::Parameters {}; +//! Find the coefficient of variation degree of the graph representation of a format object +/*! + * + * @tparam FeatureType the type in which the distribution value are returned -- + * should be a floating type + */ +template +class CoefficientOfVariationDegreeColumn + : public feature::FeaturePreprocessType { + public: + //! An empty struct used for the parameters of CoefficientOfVariationDegreeColumn + typedef CoefficientOfVariationDegreeColumnParams ParamsType; + CoefficientOfVariationDegreeColumn(); + CoefficientOfVariationDegreeColumn(CoefficientOfVariationDegreeColumnParams); + CoefficientOfVariationDegreeColumn(const CoefficientOfVariationDegreeColumn &); + CoefficientOfVariationDegreeColumn(std::shared_ptr); + virtual std::unordered_map Extract( + format::Format *format, std::vector, + bool convert_input); + virtual std::vector get_sub_ids(); + virtual std::vector get_subs(); + static std::type_index get_id_static(); + + //! Coefficient of variation degree generation executor function that carries out function + //! matching + /*! + * + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return coefficient of variation degree in the graph representation of `format` + */ + FeatureType *GetCoefficientOfVariationDegreeColumn(format::Format *format, + std::vector contexts, + bool convert_input); + //! Coefficient of variation degree generation executor function that carries out function + //! matching on a Graph + /*! + * + * @param object a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return coefficient of variation degree in the graph + */ + FeatureType *GetCoefficientOfVariationDegreeColumn( + object::Graph *object, + std::vector contexts, bool convert_input); + //! Coefficient of variation degree generation executor function that carries out function + //! matching with cached outputs + /*! + * Generates the coefficient of variation degree of the passed format. If the input format + * was converted to other format types, the converting results are also + * returned with the output @param format a single format pointer to any + * format @param contexts vector of contexts that can be used for extracting + * features. + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return A tuple with the first element being a vector of Format* + * where each pointer in the output points at the format that the corresponds + * Format object from the the input was converted to. If an input Format + * wasn't converted, the output pointer will point at nullptr. The second + * element is a pointer pointing to coefficient of variation degree + */ + std::tuple>, FeatureType *> + GetCoefficientOfVariationDegreeColumnCached(format::Format *format, + std::vector contexts, + bool convert_input); + + static FeatureType + * + //! Coefficient of variation degree generation implementation function for CSCs + /*! + * + * @param format a single format pointer to any format + * @return coefficient of variation degree in the graph representation of `formats[0]` + */ + GetCoefficientOfVariationDegreeColumnCSC(std::vector formats, + utils::Parameters *params); + ~CoefficientOfVariationDegreeColumn(); + + protected: + void Register(); +}; +} // namespace sparsebase::feature +#ifdef _HEADER_ONLY +#include "sparsebase/feature/coefficient_of_variation_degree_column.cc" +#endif +#endif // SPARSEBASE_PROJECT_COEFFICIENT_OF_VARIATION_DEGREE_COLUMN_H + diff --git a/src/sparsebase/feature/geometric_avg_degree_column.cc b/src/sparsebase/feature/geometric_avg_degree_column.cc new file mode 100644 index 00000000..e3cc4447 --- /dev/null +++ b/src/sparsebase/feature/geometric_avg_degree_column.cc @@ -0,0 +1,144 @@ +#include "sparsebase/feature/geometric_avg_degree_column.h" + +#include +#include +#include +#include +#include +#include + +#include "sparsebase/utils/parameterizable.h" + +namespace sparsebase::feature { + +template +GeometricAvgDegreeColumn::GeometricAvgDegreeColumn() { + Register(); + this->params_ = + std::shared_ptr(new GeometricAvgDegreeColumnParams()); + this->pmap_.insert({get_id_static(), this->params_}); +} +template +GeometricAvgDegreeColumn::GeometricAvgDegreeColumn( + GeometricAvgDegreeColumnParams params) { + GeometricAvgDegreeColumn(); +} + +template +GeometricAvgDegreeColumn::GeometricAvgDegreeColumn( + const GeometricAvgDegreeColumn &d) { + Register(); + this->params_ = d.params_; + this->pmap_ = d.pmap_; +} + +template +GeometricAvgDegreeColumn::GeometricAvgDegreeColumn( + const std::shared_ptr p) { + Register(); + this->params_ = p; + this->pmap_[get_id_static()] = p; +} + +template +void GeometricAvgDegreeColumn::Register() { + this->RegisterFunction( + {format::CSC::get_id_static()}, + GetGeometricAvgDegreeColumnCSC); +} + +template +std::unordered_map +GeometricAvgDegreeColumn::Extract( + format::Format *format, std::vector c, + bool convert_input) { + return {{this->get_id(), std::forward( + GetGeometricAvgDegreeColumn(format, c, convert_input))}}; +}; + +template +std::vector +GeometricAvgDegreeColumn::get_sub_ids() { + return {typeid(GeometricAvgDegreeColumn)}; +} + +template +std::vector +GeometricAvgDegreeColumn::get_subs() { + return { + new GeometricAvgDegreeColumn(*this)}; +} + +template +std::type_index +GeometricAvgDegreeColumn::get_id_static() { + return typeid(GeometricAvgDegreeColumn); +} + +template +GeometricAvgDegreeColumn::~GeometricAvgDegreeColumn() = default; + +template +std::tuple>, FeatureType *> +GeometricAvgDegreeColumn:: + GetGeometricAvgDegreeColumnCached(format::Format *format, + std::vector contexts, + bool convert_input) { + GeometricAvgDegreeColumnParams params; + return this->CachedExecute(¶ms, contexts, convert_input, false, + format); // func(sfs, this->params_.get()); +} +template +FeatureType * +GeometricAvgDegreeColumn::GetGeometricAvgDegreeColumn( + format::Format *format, std::vector contexts, + bool convert_input) { + GeometricAvgDegreeColumnParams params; + return this->Execute(¶ms, contexts, convert_input, + format); // func(sfs, this->params_.get()); +} + +template +FeatureType * +GeometricAvgDegreeColumn::GetGeometricAvgDegreeColumn( + object::Graph *obj, + std::vector contexts, bool convert_input) { + format::Format *format = obj->get_connectivity(); + return this->Execute(this->params_.get(), contexts, convert_input, + format); // func(sfs, this->params_.get()); +} + +template +FeatureType *GeometricAvgDegreeColumn:: + GetGeometricAvgDegreeColumnCSC(std::vector formats, + utils::Parameters *params) { + auto CSC = formats[0]->AsAbsolute>(); + IDType num_cols = CSC->get_dimensions()[0]; + auto *cols = CSC->get_col_ptr(); + FeatureType sum = 0.0; + for (int i = 0; i < num_cols; i++) { + sum += log(cols[i + 1] - cols[i]); + } + return new FeatureType(exp(sum/num_cols)); +} + +#if !defined(_HEADER_ONLY) +#include "init/geometric_avg_degree_column.inc" +#endif +} // namespace sparsebase::feature \ No newline at end of file diff --git a/src/sparsebase/feature/geometric_avg_degree_column.h b/src/sparsebase/feature/geometric_avg_degree_column.h new file mode 100644 index 00000000..d0c4bace --- /dev/null +++ b/src/sparsebase/feature/geometric_avg_degree_column.h @@ -0,0 +1,107 @@ +#include + +#include "sparsebase/config.h" +#include "sparsebase/feature/feature_preprocess_type.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/object/object.h" +#include "sparsebase/utils/parameterizable.h" + +#ifndef SPARSEBASE_PROJECT_GEOMETRIC_AVG_DEGREE_COLUMN_H +#define SPARSEBASE_PROJECT_GEOMETRIC_AVG_DEGREE_COLUMN_H + +namespace sparsebase::feature { + +//! An empty struct used for the parameters of GeometricAvgDegreeColumn +struct GeometricAvgDegreeColumnParams : utils::Parameters {}; +//! Find the geometric average degree of the graph representation of a format object +/*! + * + * @tparam FeatureType the type in which the distribution value are returned -- + * should be a floating type + */ +template +class GeometricAvgDegreeColumn + : public feature::FeaturePreprocessType { + public: + //! An empty struct used for the parameters of GeometricAvgDegreeColumn + typedef GeometricAvgDegreeColumnParams ParamsType; + GeometricAvgDegreeColumn(); + GeometricAvgDegreeColumn(GeometricAvgDegreeColumnParams); + GeometricAvgDegreeColumn(const GeometricAvgDegreeColumn &); + GeometricAvgDegreeColumn(std::shared_ptr); + virtual std::unordered_map Extract( + format::Format *format, std::vector, + bool convert_input); + virtual std::vector get_sub_ids(); + virtual std::vector get_subs(); + static std::type_index get_id_static(); + + //! Geometric average degree generation executor function that carries out function + //! matching + /*! + * + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return geometric average degree in the graph representation of `format` + */ + FeatureType *GetGeometricAvgDegreeColumn(format::Format *format, + std::vector contexts, + bool convert_input); + //! Geometric average degree generation executor function that carries out function + //! matching on a Graph + /*! + * + * @param object a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return geometric average degree in the graph + */ + FeatureType *GetGeometricAvgDegreeColumn( + object::Graph *object, + std::vector contexts, bool convert_input); + //! Geometric average degree generation executor function that carries out function + //! matching with cached outputs + /*! + * Generates the geometric average degree of the passed format. If the input format + * was converted to other format types, the converting results are also + * returned with the output @param format a single format pointer to any + * format @param contexts vector of contexts that can be used for extracting + * features. + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return A tuple with the first element being a vector of Format* + * where each pointer in the output points at the format that the corresponds + * Format object from the the input was converted to. If an input Format + * wasn't converted, the output pointer will point at nullptr. The second + * element is a pointer pointing to geometric average degree + */ + std::tuple>, FeatureType *> + GetGeometricAvgDegreeColumnCached(format::Format *format, + std::vector contexts, + bool convert_input); + + static FeatureType + * + //! Geometric average degree generation implementation function for CSCs + /*! + * + * @param format a single format pointer to any format + * @return geometric average degree in the graph representation of `formats[0]` + */ + GetGeometricAvgDegreeColumnCSC(std::vector formats, + utils::Parameters *params); + ~GeometricAvgDegreeColumn(); + + protected: + void Register(); +}; +} // namespace sparsebase::feature +#ifdef _HEADER_ONLY +#include "sparsebase/feature/geometric_avg_degree_column.cc" +#endif + +#endif // SPARSEBASE_PROJECT_GEOMETRIC_AVG_DEGREE_COLUMN_H diff --git a/src/sparsebase/feature/max_degree_column.cc b/src/sparsebase/feature/max_degree_column.cc new file mode 100644 index 00000000..87ee07b0 --- /dev/null +++ b/src/sparsebase/feature/max_degree_column.cc @@ -0,0 +1,109 @@ +#include "max_degree_column.h" + +#include +#include +#include +#include +#include + +#include "sparsebase/utils/parameterizable.h" + +namespace sparsebase::feature { + +template +MaxDegreeColumn::MaxDegreeColumn(ParamsType) { + MaxDegreeColumn(); +} +template +MaxDegreeColumn::MaxDegreeColumn() { + Register(); + this->params_ = std::shared_ptr(new ParamsType()); + this->pmap_.insert({get_id_static(), this->params_}); +} + +template +MaxDegreeColumn::MaxDegreeColumn( + const MaxDegreeColumn &d) { + Register(); + this->params_ = d.params_; + this->pmap_ = d.pmap_; +} + +template +MaxDegreeColumn::MaxDegreeColumn( + const std::shared_ptr r) { + Register(); + this->params_ = r; + this->pmap_[get_id_static()] = r; +} + +template +MaxDegreeColumn::~MaxDegreeColumn() = default; + +template +void MaxDegreeColumn::Register() { + this->RegisterFunction( + {format::CSC::get_id_static()}, + GetMaxDegreeColumnCSC); +} + +template +std::vector +MaxDegreeColumn::get_sub_ids() { + return {typeid(MaxDegreeColumn)}; +} + +template +std::vector +MaxDegreeColumn::get_subs() { + return {new MaxDegreeColumn(*this)}; +} + +template +std::type_index MaxDegreeColumn::get_id_static() { + return typeid(MaxDegreeColumn); +} + +template +std::unordered_map +MaxDegreeColumn::Extract(format::Format *format, + std::vector c, + bool convert_input) { + return {{this->get_id(), + std::forward(GetMaxDegreeColumn(format, c, convert_input))}}; +}; + +template +NNZType *MaxDegreeColumn::GetMaxDegreeColumn( + format::Format *format, std::vector c, + bool convert_input) { + return this->Execute(this->params_.get(), c, convert_input, format); +} + +template +std::tuple>, NNZType *> +MaxDegreeColumn::GetMaxDegreeColumnCached( + format::Format *format, std::vector c, + bool convert_input) { + return this->CachedExecute(this->params_.get(), c, convert_input, false, + format); +} + +template +NNZType *MaxDegreeColumn::GetMaxDegreeColumnCSC( + std::vector formats, utils::Parameters *params) { + auto csc = formats[0]->AsAbsolute>(); + IDType num_col = csc->get_dimensions()[0]; + auto *cols = csc->get_col_ptr(); + NNZType *max_degree = new NNZType; + *max_degree = cols[1] - cols[0]; + for (int i = 1; i < num_col; i++) { + *max_degree = std::max(*max_degree, cols[i + 1] - cols[i]); + } + return max_degree; +} + +#if !defined(_HEADER_ONLY) +#include "init/max_degree_column.inc" +#endif +} // namespace sparsebase::feature \ No newline at end of file diff --git a/src/sparsebase/feature/max_degree_column.h b/src/sparsebase/feature/max_degree_column.h new file mode 100644 index 00000000..e6438114 --- /dev/null +++ b/src/sparsebase/feature/max_degree_column.h @@ -0,0 +1,80 @@ +#include + +#include "sparsebase/config.h" +#include "sparsebase/feature/feature_preprocess_type.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/utils/parameterizable.h" + +#ifndef SPARSEBASE_PROJECT_MAX_DEGREE_COLUMN_H +#define SPARSEBASE_PROJECT_MAX_DEGREE_COLUMN_H + +namespace sparsebase::feature { +//! Find the max degree in the graph representation of a format object +template +class MaxDegreeColumn : public feature::FeaturePreprocessType { + public: + //! An empty struct used for the parameters of Max Degree + typedef utils::Parameters ParamsType; + MaxDegreeColumn(); + MaxDegreeColumn(ParamsType); + MaxDegreeColumn(const MaxDegreeColumn &d); + MaxDegreeColumn(std::shared_ptr); + std::unordered_map Extract( + format::Format *format, std::vector, + bool convert_input) override; + std::vector get_sub_ids() override; + std::vector get_subs() override; + static std::type_index get_id_static(); + + //! Max Degree Column executor function that carries out function matching + /*! + * + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * features. + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return max degree column in the graph representation of `format` + */ + NNZType *GetMaxDegreeColumn(format::Format *format, + std::vector contexts, + bool convert_input); + std:: + tuple>, NNZType *> + //! Max Degree Column executor function that carries out function + //! matching with cached output + /*! + * + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * features. + * @param convert_input whether or not to convert the input format if that + * is needed. + * @return max degree in the graph representation of `format` + */ + GetMaxDegreeColumnCached(format::Format *format, + std::vector contexts, + bool convert_input); + //! Max Degree Column implementation function for CSCs + /*! + * + * @param formats A vector containing a single format pointer that should + * point at a CSR object + * @param params a Parameters pointer, though it + * is not used in the function + * @return max degree in the graph representations of 'format[0]' + */ + static NNZType *GetMaxDegreeColumnCSC(std::vector formats, + utils::Parameters *params); + ~MaxDegreeColumn(); + + protected: + void Register(); +}; + +} // namespace sparsebase::feature +#ifdef _HEADER_ONLY +#include "sparsebase/feature/max_degree_column.cc" +#endif + +#endif // SPARSEBASE_PROJECT_MAX_DEGREE_COLUMN_H diff --git a/src/sparsebase/feature/median_degree_column.cc b/src/sparsebase/feature/median_degree_column.cc new file mode 100644 index 00000000..e68f8b15 --- /dev/null +++ b/src/sparsebase/feature/median_degree_column.cc @@ -0,0 +1,156 @@ +// +// Created by Sinan Ekmekcibasi on 8.11.2023. +// + +#include "sparsebase/feature/median_degree_column.h" + +#include +#include +#include +#include +#include + +#include "sparsebase/utils/parameterizable.h" + +namespace sparsebase::feature { + +template +MedianDegreeColumn::MedianDegreeColumn() { + Register(); + this->params_ = + std::shared_ptr(new MedianDegreeColumnParams()); + this->pmap_.insert({get_id_static(), this->params_}); +} +template +MedianDegreeColumn::MedianDegreeColumn( + MedianDegreeColumnParams params) { + MedianDegreeColumn(); +} + +template +MedianDegreeColumn::MedianDegreeColumn( + const MedianDegreeColumn &d) { + Register(); + this->params_ = d.params_; + this->pmap_ = d.pmap_; +} + +template +MedianDegreeColumn::MedianDegreeColumn( + const std::shared_ptr p) { + Register(); + this->params_ = p; + this->pmap_[get_id_static()] = p; +} + +template +void MedianDegreeColumn::Register() { + this->RegisterFunction( + {format::CSR::get_id_static()}, + GetMedianDegreeColumnCSC); +} + +template +std::unordered_map +MedianDegreeColumn::Extract( + format::Format *format, std::vector c, + bool convert_input) { + return {{this->get_id(), std::forward( + GetMedianDegreeColumn(format, c, convert_input))}}; +}; + +template +std::vector +MedianDegreeColumn::get_sub_ids() { + return {typeid(MedianDegreeColumn)}; +} + +template +std::vector +MedianDegreeColumn::get_subs() { + return { + new MedianDegreeColumn(*this)}; +} + +template +std::type_index +MedianDegreeColumn::get_id_static() { + return typeid(MedianDegreeColumn); +} + +template +MedianDegreeColumn::~MedianDegreeColumn() = default; + +template +std::tuple>, FeatureType *> +MedianDegreeColumn:: + GetMedianDegreeColumnCached(format::Format *format, + std::vector contexts, + bool convert_input) { + MedianDegreeColumnParams params; + return this->CachedExecute(¶ms, contexts, convert_input, false, + format); // func(sfs, this->params_.get()); +} +template +FeatureType * +MedianDegreeColumn::GetMedianDegreeColumn( + format::Format *format, std::vector contexts, + bool convert_input) { + MedianDegreeColumnParams params; + return this->Execute(¶ms, contexts, convert_input, + format); // func(sfs, this->params_.get()); +} + +template +FeatureType * +MedianDegreeColumn::GetMedianDegreeColumn( + object::Graph *obj, + std::vector contexts, bool convert_input) { + format::Format *format = obj->get_connectivity(); + return this->Execute(this->params_.get(), contexts, convert_input, + format); // func(sfs, this->params_.get()); +} + +template +FeatureType *MedianDegreeColumn:: + GetMedianDegreeColumnCSC(std::vector formats, + utils::Parameters *params) { + auto csc = formats[0]->AsAbsolute>(); + IDType num_col = csc->get_dimensions()[0]; + auto *cols = csc->get_col_ptr(); + + std::vector degrees; + degrees.reserve(num_col); + for (IDType i = 0; i < num_col; i++) { + NNZType degree = cols[i + 1] - cols[i]; + degrees.push_back(degree); + } + std::sort(degrees.begin(), degrees.end()); + if (num_col % 2 == 0) { + return new FeatureType((FeatureType)(degrees[num_col / 2 - 1] + degrees[num_col / 2]) / 2.0); + } + else { + return new FeatureType(degrees[num_col / 2]); + } +} + +#if !defined(_HEADER_ONLY) +#include "init/median_degree_column.inc" +#endif +} // namespace sparsebase::feature \ No newline at end of file diff --git a/src/sparsebase/feature/median_degree_column.h b/src/sparsebase/feature/median_degree_column.h new file mode 100644 index 00000000..b2ca5513 --- /dev/null +++ b/src/sparsebase/feature/median_degree_column.h @@ -0,0 +1,107 @@ +#include + +#include "sparsebase/config.h" +#include "sparsebase/feature/feature_preprocess_type.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/object/object.h" +#include "sparsebase/utils/parameterizable.h" + +#ifndef SPARSEBASE_PROJECT_MEDIAN_DEGREE_COLUMN_H +#define SPARSEBASE_PROJECT_MEDIAN_DEGREE_COLUMN_H + +namespace sparsebase::feature { + +//! An empty struct used for the parameters of MedianDegree +struct MedianDegreeColumnParams : utils::Parameters {}; +//! Find the median degree of the graph representation of a format object +/*! + * + * @tparam FeatureType the type in which the distribution value are returned -- + * should be a floating type + */ +template +class MedianDegreeColumn + : public feature::FeaturePreprocessType { + public: + //! An empty struct used for the parameters of MedianDegree + typedef MedianDegreeColumnParams ParamsType; + MedianDegreeColumn(); + MedianDegreeColumn(MedianDegreeColumnParams); + MedianDegreeColumn(const MedianDegreeColumn &); + MedianDegreeColumn(std::shared_ptr); + virtual std::unordered_map Extract( + format::Format *format, std::vector, + bool convert_input); + virtual std::vector get_sub_ids(); + virtual std::vector get_subs(); + static std::type_index get_id_static(); + + //! Median degree generation executor function that carries out function + //! matching + /*! + * + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return median degree in the graph representation of `format` + */ + FeatureType *GetMedianDegreeColumn(format::Format *format, + std::vector contexts, + bool convert_input); + //! Median degree generation executor function that carries out function + //! matching on a Graph + /*! + * + * @param object a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return median degree in the graph + */ + FeatureType *GetMedianDegreeColumn( + object::Graph *object, + std::vector contexts, bool convert_input); + //! Median degree generation executor function that carries out function + //! matching with cached outputs + /*! + * Generates the median degree of the passed format. If the input format + * was converted to other format types, the converting results are also + * returned with the output @param format a single format pointer to any + * format @param contexts vector of contexts that can be used for extracting + * features. + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return A tuple with the first element being a vector of Format* + * where each pointer in the output points at the format that the corresponds + * Format object from the the input was converted to. If an input Format + * wasn't converted, the output pointer will point at nullptr. The second + * element is a pointer pointing to median degree + */ + std::tuple>, FeatureType *> + GetMedianDegreeColumnCached(format::Format *format, + std::vector contexts, + bool convert_input); + + static FeatureType + * + //! Median degree generation implementation function for CSRs + /*! + * + * @param format a single format pointer to any format + * @return median degree in the graph representation of `formats[0]` + */ + GetMedianDegreeColumnCSC(std::vector formats, + utils::Parameters *params); + ~MedianDegreeColumn(); + + protected: + void Register(); +}; +} // namespace sparsebase::feature +#ifdef _HEADER_ONLY +#include "sparsebase/feature/median_degree_column.cc" +#endif + +#endif // SPARSEBASE_PROJECT_MEDIAN_DEGREE_COLUMN_H diff --git a/src/sparsebase/feature/min_degree_column.cc b/src/sparsebase/feature/min_degree_column.cc new file mode 100644 index 00000000..42e096c4 --- /dev/null +++ b/src/sparsebase/feature/min_degree_column.cc @@ -0,0 +1,109 @@ +#include "min_degree_column.h" + +#include +#include +#include +#include +#include + +#include "sparsebase/utils/parameterizable.h" + +namespace sparsebase::feature { + +template +MinDegreeColumn::MinDegreeColumn(ParamsType) { + MinDegreeColumn(); +} +template +MinDegreeColumn::MinDegreeColumn() { + Register(); + this->params_ = std::shared_ptr(new ParamsType()); + this->pmap_.insert({get_id_static(), this->params_}); +} + +template +MinDegreeColumn::MinDegreeColumn( + const MinDegreeColumn &d) { + Register(); + this->params_ = d.params_; + this->pmap_ = d.pmap_; +} + +template +MinDegreeColumn::MinDegreeColumn( + const std::shared_ptr r) { + Register(); + this->params_ = r; + this->pmap_[get_id_static()] = r; +} + +template +MinDegreeColumn::~MinDegreeColumn() = default; + +template +void MinDegreeColumn::Register() { + this->RegisterFunction( + {format::CSC::get_id_static()}, + GetMinDegreeColumnCSC); +} + +template +std::vector +MinDegreeColumn::get_sub_ids() { + return {typeid(MinDegreeColumn)}; +} + +template +std::vector +MinDegreeColumn::get_subs() { + return {new MinDegreeColumn(*this)}; +} + +template +std::type_index MinDegreeColumn::get_id_static() { + return typeid(MinDegreeColumn); +} + +template +std::unordered_map +MinDegreeColumn::Extract(format::Format *format, + std::vector c, + bool convert_input) { + return {{this->get_id(), + std::forward(GetMinDegreeColumn(format, c, convert_input))}}; +}; + +template +NNZType *MinDegreeColumn::GetMinDegreeColumn( + format::Format *format, std::vector c, + bool convert_input) { + return this->Execute(this->params_.get(), c, convert_input, format); +} + +template +std::tuple>, NNZType *> +MinDegreeColumn::GetMinDegreeColumnCached( + format::Format *format, std::vector c, + bool convert_input) { + return this->CachedExecute(this->params_.get(), c, convert_input, false, + format); +} + +template +NNZType *MinDegreeColumn::GetMinDegreeColumnCSC( + std::vector formats, utils::Parameters *params) { + auto csc = formats[0]->AsAbsolute>(); + IDType num_col = csc->get_dimensions()[0]; + auto *cols = csc->get_col_ptr(); + NNZType *min_degree = new NNZType; + *min_degree = cols[1] - cols[0]; + for (int i = 1; i < num_col; i++) { + *min_degree = std::min(*min_degree, cols[i + 1] - cols[i]); + } + return min_degree; +} + +#if !defined(_HEADER_ONLY) +#include "init/min_degree_column.inc" +#endif +} // namespace sparsebase::feature \ No newline at end of file diff --git a/src/sparsebase/feature/min_degree_column.h b/src/sparsebase/feature/min_degree_column.h new file mode 100644 index 00000000..c7c67a78 --- /dev/null +++ b/src/sparsebase/feature/min_degree_column.h @@ -0,0 +1,80 @@ +#include + +#include "sparsebase/config.h" +#include "sparsebase/feature/feature_preprocess_type.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/utils/parameterizable.h" + +#ifndef SPARSEBASE_PROJECT_MIN_DEGREE_COLUMN_H +#define SPARSEBASE_PROJECT_MIN_DEGREE_COLUMN_H + +namespace sparsebase::feature { +//! Find the min degree in the graph representation of a format object +template +class MinDegreeColumn : public feature::FeaturePreprocessType { + public: + //! An empty struct used for the parameters of Min Degree + typedef utils::Parameters ParamsType; + MinDegreeColumn(); + MinDegreeColumn(ParamsType); + MinDegreeColumn(const MinDegreeColumn &d); + MinDegreeColumn(std::shared_ptr); + std::unordered_map Extract( + format::Format *format, std::vector, + bool convert_input) override; + std::vector get_sub_ids() override; + std::vector get_subs() override; + static std::type_index get_id_static(); + + //! Min Degree executor function that carries out function matching + /*! + * + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * features. + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return min degree in the graph representation of `format` + */ + NNZType *GetMinDegreeColumn(format::Format *format, + std::vector contexts, + bool convert_input); + std:: + tuple>, NNZType *> + //! Min Degree executor function that carries out function + //! matching with cached output + /*! + * + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * features. + * @param convert_input whether or not to convert the input format if that + * is needed. + * @return min degree in the graph representation of `format` + */ + GetMinDegreeColumnCached(format::Format *format, + std::vector contexts, + bool convert_input); + //! Min Degree implementation function for CSCs + /*! + * + * @param formats A vector containing a single format pointer that should + * point at a CSR object + * @param params a Parameters pointer, though it + * is not used in the function + * @return min degree in the graph representations of 'format[0]' + */ + static NNZType *GetMinDegreeColumnCSC(std::vector formats, + utils::Parameters *params); + ~MinDegreeColumn(); + + protected: + void Register(); +}; + +} // namespace sparsebase::feature +#ifdef _HEADER_ONLY +#include "sparsebase/feature/min_degree_column.cc" +#endif + +#endif // SPARSEBASE_PROJECT_MIN_DEGREE_COLUMN_H diff --git a/src/sparsebase/feature/off_diag_block_nnz.cc b/src/sparsebase/feature/off_diag_block_nnz.cc new file mode 100644 index 00000000..cf4799d2 --- /dev/null +++ b/src/sparsebase/feature/off_diag_block_nnz.cc @@ -0,0 +1,121 @@ +#include +#include +#include +#include +#include + +#include "sparsebase/feature/off_diag_block_nnz.h" +#include "sparsebase/utils/parameterizable.h" + +namespace sparsebase::feature { + +template +OffDiagBlockNNZ::OffDiagBlockNNZ(ParamsType params) { + Register(); + this->params_ = std::shared_ptr(new ParamsType(params.blockrowsize, params.blockcolsize)); + this->pmap_.insert({get_id_static(), this->params_}); +} +template +OffDiagBlockNNZ::OffDiagBlockNNZ() { + Register(); + this->params_ = std::shared_ptr(new ParamsType()); + this->pmap_.insert({get_id_static(), this->params_}); +} + +template +OffDiagBlockNNZ::OffDiagBlockNNZ( + const OffDiagBlockNNZ &d) { + Register(); + this->params_ = d.params_; + this->pmap_ = d.pmap_; +} + +template +OffDiagBlockNNZ::OffDiagBlockNNZ( + const std::shared_ptr p) { + Register(); + this->params_ = p; + this->pmap_[get_id_static()] = p; +} + +template +OffDiagBlockNNZ::~OffDiagBlockNNZ() = default; + +template +void OffDiagBlockNNZ::Register() { + this->RegisterFunction( + {format::CSR::get_id_static()}, + GetOffDiagBlockNNZCSR); +} + +template +std::vector +OffDiagBlockNNZ::get_sub_ids() { + return {typeid(OffDiagBlockNNZ)}; +} + +template +std::vector +OffDiagBlockNNZ::get_subs() { + return {new OffDiagBlockNNZ(*this)}; +} + +template +std::type_index OffDiagBlockNNZ::get_id_static() { + return typeid(OffDiagBlockNNZ); +} + +template +std::unordered_map +OffDiagBlockNNZ::Extract(format::Format *format, + std::vector c, + bool convert_input) { + return {{this->get_id(), + std::forward(GetOffDiagBlockNNZ(format, c, convert_input))}}; +}; + +template +IDType *OffDiagBlockNNZ::GetOffDiagBlockNNZ( + format::Format *format, std::vector c, + bool convert_input) { + return this->Execute(this->params_.get(), c, convert_input, format); +} + +template +std::tuple>, IDType *> +OffDiagBlockNNZ::GetOffDiagBlockNNZCached( + format::Format *format, std::vector c, + bool convert_input) { + return this->CachedExecute(this->params_.get(), c, convert_input, false, + format); +} + +template +IDType *OffDiagBlockNNZ::GetOffDiagBlockNNZCSR( + std::vector formats, utils::Parameters *params) { + OffDiagBlockNNZParams* param = static_cast(params); + int h = param->blockrowsize, w = param->blockcolsize; + auto csr = formats[0]->AsAbsolute>(); + IDType cnt = 0; + int num_rows = csr->get_dimensions()[0]; + int num_cols = csr->get_dimensions()[1]; + auto row_ptr = csr->get_row_ptr(); + auto col = csr->get_col(); + for (int p = 0; p < h; p++) { + IDType rowstart = std::min(num_rows, p*(num_rows/h) + std::min(p, num_rows%h)); + IDType rowend = std::min(num_rows, (p+1)*(num_rows/h) + std::min(p+1, num_rows%h)); + IDType colstart = std::min(num_cols, p*(num_cols/w) + std::min(p, num_cols%w)); + IDType colend = std::min(num_cols, (p+1)*(num_cols/w) + std::min(p+1, num_cols%w)); + for (IDType i = rowstart; i < rowend; i++) { + for (IDType k = row_ptr[i]; k < row_ptr[i+1]; k++) { + if (col[k] < colstart || col[k] >= colend) ++cnt; + } + } + } + return new IDType(cnt); +} + +#if !defined(_HEADER_ONLY) +#include "init/off_diag_block_nnz.inc" +#endif +} // namespace sparsebase::feature \ No newline at end of file diff --git a/src/sparsebase/feature/off_diag_block_nnz.h b/src/sparsebase/feature/off_diag_block_nnz.h new file mode 100644 index 00000000..679bc285 --- /dev/null +++ b/src/sparsebase/feature/off_diag_block_nnz.h @@ -0,0 +1,74 @@ +#include + +#include "sparsebase/config.h" +#include "sparsebase/feature/feature_preprocess_type.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/utils/parameterizable.h" + +#ifndef SPARSEBASE_PROJECT_OFF_DIAG_BLOCK_NNZ_H +#define SPARSEBASE_PROJECT_OFF_DIAG_BLOCK_NNZ_H +namespace sparsebase::feature { + +struct OffDiagBlockNNZParams : utils::Parameters { + int blockrowsize = 1, blockcolsize = 1; + OffDiagBlockNNZParams() {} + OffDiagBlockNNZParams(int N) : blockrowsize(N), blockcolsize(N){} + OffDiagBlockNNZParams(int blockrowsize, int blockcolsize) + : blockrowsize(blockrowsize), blockcolsize(blockcolsize) {} +}; + +//! Find the number of nonzeros in off-diagonal blocks of partitioned matrix, in the graph representation of a format object +template +class OffDiagBlockNNZ : public feature::FeaturePreprocessType { + public: + typedef OffDiagBlockNNZParams ParamsType; + OffDiagBlockNNZ(); + OffDiagBlockNNZ(ParamsType); + OffDiagBlockNNZ(const OffDiagBlockNNZ &d); + OffDiagBlockNNZ(std::shared_ptr); + std::unordered_map Extract( + format::Format *format, std::vector, + bool convert_input) override; + std::vector get_sub_ids() override; + std::vector get_subs() override; + static std::type_index get_id_static(); + + //! OffDiagBlockNNZ executor function that carries out function matching + /*! + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting features. + * @param convert_input whether or not to convert the input format if that is needed. + * @return OffDiagBlockNNZ in the graph representation of `format` + */ + IDType *GetOffDiagBlockNNZ(format::Format *format, std::vector contexts, + bool convert_input); + + std::tuple>, IDType *> + //! OffDiagBlockNNZ executor function that carries out function matching with cached output + /*! + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting features. + * @param convert_input whether or not to convert the input format if that is needed. + * @return OffDiagBlockNNZ in the graph representation of `format` + */ + GetOffDiagBlockNNZCached(format::Format *format, std::vector contexts, + bool convert_input); + //! OffDiagBlockNNZ implementation function for CSRs + /*! + * @param formats A vector containing a single format pointer that should point at a CSR object + * @param params a Parameters pointer, though it is not used in the function + * @return OffDiagBlockNNZ in the graph representations of 'format[0]' + */ + static IDType *GetOffDiagBlockNNZCSR(std::vector formats, utils::Parameters *params); + + ~OffDiagBlockNNZ(); + + protected: + void Register(); +}; + +} // namespace sparsebase::feature +#ifdef _HEADER_ONLY +#include "sparsebase/feature/off_diag_block_nnz.cc" +#endif +#endif // SPARSEBASE_PROJECT_OFF_DIAG_BLOCK_NNZ_H diff --git a/src/sparsebase/feature/profile.cc b/src/sparsebase/feature/profile.cc new file mode 100644 index 00000000..0dd5d9fd --- /dev/null +++ b/src/sparsebase/feature/profile.cc @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include + +#include "sparsebase/feature/profile.h" +#include "sparsebase/utils/parameterizable.h" + +namespace sparsebase::feature { + +template +Profile::Profile(ParamsType params) { + Profile(); +} +template +Profile::Profile() { + Register(); + this->params_ = std::shared_ptr(new ParamsType()); + this->pmap_.insert({get_id_static(), this->params_}); +} + +template +Profile::Profile( + const Profile &d) { + Register(); + this->params_ = d.params_; + this->pmap_ = d.pmap_; +} + +template +Profile::Profile( + const std::shared_ptr p) { + Register(); + this->params_ = p; + this->pmap_[get_id_static()] = p; +} + +template +Profile::~Profile() = default; + +template +void Profile::Register() { + this->RegisterFunction( + {format::CSR::get_id_static()}, + GetProfileCSR); +} + +template +std::vector +Profile::get_sub_ids() { + return {typeid(Profile)}; +} + +template +std::vector +Profile::get_subs() { + return {new Profile(*this)}; +} + +template +std::type_index Profile::get_id_static() { + return typeid(Profile); +} + +template +std::unordered_map +Profile::Extract(format::Format *format, + std::vector c, + bool convert_input) { + return {{this->get_id(), + std::forward(GetProfile(format, c, convert_input))}}; +}; + +template +IDType *Profile::GetProfile( + format::Format *format, std::vector c, + bool convert_input) { + return this->Execute(this->params_.get(), c, convert_input, format); +} + +template +std::tuple>, IDType *> +Profile::GetProfileCached( + format::Format *format, std::vector c, + bool convert_input) { + return this->CachedExecute(this->params_.get(), c, convert_input, false, + format); +} + +template +IDType *Profile::GetProfileCSR( + std::vector formats, utils::Parameters *params) { + auto csr = formats[0]->AsAbsolute>(); + auto row_ptr = csr->get_row_ptr(); + auto col = csr->get_col(); + IDType sum = 0; + for (int i = 0; i < csr->get_dimensions()[0]; ++i) { + IDType j = i; + for (int k = row_ptr[i]; k < row_ptr[i+1]; ++k) { + if (j > col[k]) j = col[k]; + } + sum += i-j; + } + return new IDType(sum); +} + +#if !defined(_HEADER_ONLY) +#include "init/profile.inc" +#endif +} // namespace sparsebase::feature \ No newline at end of file diff --git a/src/sparsebase/feature/profile.h b/src/sparsebase/feature/profile.h new file mode 100644 index 00000000..6a735df6 --- /dev/null +++ b/src/sparsebase/feature/profile.h @@ -0,0 +1,67 @@ +#include + +#include "sparsebase/config.h" +#include "sparsebase/feature/feature_preprocess_type.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/utils/parameterizable.h" + +#ifndef SPARSEBASE_PROJECT_PROFILE_H +#define SPARSEBASE_PROJECT_PROFILE_H +namespace sparsebase::feature { + +//! Compute profile in the graph representation of a format object +template +class Profile : public feature::FeaturePreprocessType { + public: + //! An empty struct used for the parameters of Profile Feature + typedef utils::Parameters ParamsType; + Profile(); + Profile(ParamsType); + Profile(const Profile &d); + Profile(std::shared_ptr); + std::unordered_map Extract( + format::Format *format, std::vector, + bool convert_input) override; + std::vector get_sub_ids() override; + std::vector get_subs() override; + static std::type_index get_id_static(); + + //! Profile executor function that carries out function matching + /*! + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting features. + * @param convert_input whether or not to convert the input format if that is needed. + * @return Profile in the graph representation of `format` + */ + IDType *GetProfile(format::Format *format, std::vector contexts, + bool convert_input); + + std::tuple>, IDType *> + //! Profile executor function that carries out function matching with cached output + /*! + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting features. + * @param convert_input whether or not to convert the input format if that is needed. + * @return Profile in the graph representation of `format` + */ + GetProfileCached(format::Format *format, std::vector contexts, + bool convert_input); + //! Profile implementation function for CSRs + /*! + * @param formats A vector containing a single format pointer that should point at a CSR object + * @param params a Parameters pointer, though it is not used in the function + * @return Profile in the graph representations of 'format[0]' + */ + static IDType *GetProfileCSR(std::vector formats, utils::Parameters *params); + + ~Profile(); + + protected: + void Register(); +}; + +} // namespace sparsebase::feature +#ifdef _HEADER_ONLY +#include "sparsebase/feature/profile.cc" +#endif +#endif // SPARSEBASE_PROJECT_PROFILE_H diff --git a/src/sparsebase/feature/standard_deviation_degree_column.cc b/src/sparsebase/feature/standard_deviation_degree_column.cc new file mode 100644 index 00000000..3389cce0 --- /dev/null +++ b/src/sparsebase/feature/standard_deviation_degree_column.cc @@ -0,0 +1,148 @@ +#include "sparsebase/feature/standard_deviation_degree_column.h" + +#include +#include +#include +#include +#include +#include + +#include "sparsebase/utils/parameterizable.h" + +namespace sparsebase::feature { + +template +StandardDeviationDegreeColumn::StandardDeviationDegreeColumn() { + Register(); + this->params_ = + std::shared_ptr(new StandardDeviationDegreeColumnParams()); + this->pmap_.insert({get_id_static(), this->params_}); +} +template +StandardDeviationDegreeColumn::StandardDeviationDegreeColumn( + StandardDeviationDegreeColumnParams params) { + StandardDeviationDegreeColumn(); +} + +template +StandardDeviationDegreeColumn::StandardDeviationDegreeColumn( + const StandardDeviationDegreeColumn &d) { + Register(); + this->params_ = d.params_; + this->pmap_ = d.pmap_; +} + +template +StandardDeviationDegreeColumn::StandardDeviationDegreeColumn( + const std::shared_ptr p) { + Register(); + this->params_ = p; + this->pmap_[get_id_static()] = p; +} + +template +void StandardDeviationDegreeColumn::Register() { + this->RegisterFunction( + {format::CSC::get_id_static()}, + GetStandardDeviationDegreeColumnCSC); +} + +template +std::unordered_map +StandardDeviationDegreeColumn::Extract( + format::Format *format, std::vector c, + bool convert_input) { + return {{this->get_id(), std::forward( + GetStandardDeviationDegreeColumn(format, c, convert_input))}}; +}; + +template +std::vector +StandardDeviationDegreeColumn::get_sub_ids() { + return {typeid(StandardDeviationDegreeColumn)}; +} + +template +std::vector +StandardDeviationDegreeColumn::get_subs() { + return { + new StandardDeviationDegreeColumn(*this)}; +} + +template +std::type_index +StandardDeviationDegreeColumn::get_id_static() { + return typeid(StandardDeviationDegreeColumn); +} + +template +StandardDeviationDegreeColumn::~StandardDeviationDegreeColumn() = default; + +template +std::tuple>, FeatureType *> +StandardDeviationDegreeColumn:: + GetStandardDeviationDegreeColumnCached(format::Format *format, + std::vector contexts, + bool convert_input) { + StandardDeviationDegreeColumnParams params; + return this->CachedExecute(¶ms, contexts, convert_input, false, + format); // func(sfs, this->params_.get()); +} +template +FeatureType * +StandardDeviationDegreeColumn::GetStandardDeviationDegreeColumn( + format::Format *format, std::vector contexts, + bool convert_input) { + StandardDeviationDegreeColumnParams params; + return this->Execute(¶ms, contexts, convert_input, + format); // func(sfs, this->params_.get()); +} + +template +FeatureType * +StandardDeviationDegreeColumn::GetStandardDeviationDegreeColumn( + object::Graph *obj, + std::vector contexts, bool convert_input) { + format::Format *format = obj->get_connectivity(); + return this->Execute(this->params_.get(), contexts, convert_input, + format); // func(sfs, this->params_.get()); +} + +template +FeatureType *StandardDeviationDegreeColumn:: + GetStandardDeviationDegreeColumnCSC(std::vector formats, + utils::Parameters *params) { + auto CSC = formats[0]->AsAbsolute>(); + IDType num_cols = CSC->get_dimensions()[0]; + FeatureType avg_degree; + auto *cols = CSC->get_col_ptr(); + NNZType degree_sum = cols[num_cols] - cols[0]; + avg_degree = degree_sum / (FeatureType)num_cols; + FeatureType standard_deviation_degree = 0; + for (int i = 0; i < num_cols; i++) { + standard_deviation_degree += (cols[i + 1] - cols[i] - avg_degree)*(cols[i + 1] - cols[i] - avg_degree); + } + return new FeatureType(sqrt(standard_deviation_degree)); +} + +#if !defined(_HEADER_ONLY) +#include "init/standard_deviation_degree_column.inc" +#endif +} // namespace sparsebase::feature + diff --git a/src/sparsebase/feature/standard_deviation_degree_column.h b/src/sparsebase/feature/standard_deviation_degree_column.h new file mode 100644 index 00000000..0fe9845e --- /dev/null +++ b/src/sparsebase/feature/standard_deviation_degree_column.h @@ -0,0 +1,105 @@ +#include + +#include "sparsebase/config.h" +#include "sparsebase/feature/feature_preprocess_type.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/object/object.h" +#include "sparsebase/utils/parameterizable.h" + +#ifndef SPARSEBASE_PROJECT_STANDARD_DEVIATION_DEGREE_COLUMN_H +#define SPARSEBASE_PROJECT_STANDARD_DEVIATION_DEGREE_COLUMN_H +namespace sparsebase::feature { + +//! An empty struct used for the parameters of StandardDeviationDegreeColumn +struct StandardDeviationDegreeColumnParams : utils::Parameters {}; +//! Find the standard deviation degree of the graph representation of a format object +/*! + * + * @tparam FeatureType the type in which the distribution value are returned -- + * should be a floating type + */ +template +class StandardDeviationDegreeColumn + : public feature::FeaturePreprocessType { + public: + //! An empty struct used for the parameters of StandardDeviationDegreeColumn + typedef StandardDeviationDegreeColumnParams ParamsType; + StandardDeviationDegreeColumn(); + StandardDeviationDegreeColumn(StandardDeviationDegreeColumnParams); + StandardDeviationDegreeColumn(const StandardDeviationDegreeColumn &); + StandardDeviationDegreeColumn(std::shared_ptr); + virtual std::unordered_map Extract( + format::Format *format, std::vector, + bool convert_input); + virtual std::vector get_sub_ids(); + virtual std::vector get_subs(); + static std::type_index get_id_static(); + + //! Standard deviation degree generation executor function that carries out function + //! matching + /*! + * + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return standard deviation degree in the graph representation of `format` + */ + FeatureType *GetStandardDeviationDegreeColumn(format::Format *format, + std::vector contexts, + bool convert_input); + //! Standard deviation degree generation executor function that carries out function + //! matching on a Graph + /*! + * + * @param object a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return standard deviation degree in the graph + */ + FeatureType *GetStandardDeviationDegreeColumn( + object::Graph *object, + std::vector contexts, bool convert_input); + //! Standard deviation degree generation executor function that carries out function + //! matching with cached outputs + /*! + * Generates the standard deviation degree of the passed format. If the input format + * was converted to other format types, the converting results are also + * returned with the output @param format a single format pointer to any + * format @param contexts vector of contexts that can be used for extracting + * features. + * @param convert_input whether or not to convert the input format if that is + * needed. + * @return A tuple with the first element being a vector of Format* + * where each pointer in the output points at the format that the corresponds + * Format object from the the input was converted to. If an input Format + * wasn't converted, the output pointer will point at nullptr. The second + * element is a pointer pointing to standard deviation degree + */ + std::tuple>, FeatureType *> + GetStandardDeviationDegreeColumnCached(format::Format *format, + std::vector contexts, + bool convert_input); + + static FeatureType + * + //! Standard deviation degree generation implementation function for CSCs + /*! + * + * @param format a single format pointer to any format + * @return standard deviation degree in the graph representation of `formats[0]` + */ + GetStandardDeviationDegreeColumnCSC(std::vector formats, + utils::Parameters *params); + ~StandardDeviationDegreeColumn(); + + protected: + void Register(); +}; +} // namespace sparsebase::feature +#ifdef _HEADER_ONLY +#include "standard_deviation_degree_column.cc" +#endif +#endif // SPARSEBASE_PROJECT_STANDARD_DEVIATION_DEGREE_COLUMN_H diff --git a/src/sparsebase/feature/triangle_count.cc b/src/sparsebase/feature/triangle_count.cc new file mode 100644 index 00000000..94584bf9 --- /dev/null +++ b/src/sparsebase/feature/triangle_count.cc @@ -0,0 +1,228 @@ +#include "sparsebase/feature/triangle_count.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "sparsebase/utils/parameterizable.h" + +namespace sparsebase::feature { + +template +TriangleCount::TriangleCount(ParamsType p) { + Register(); + this->params_ = std::shared_ptr(new ParamsType(p.countDirected)); + this->pmap_.insert({get_id_static(), this->params_}); +} + +template +TriangleCount::TriangleCount() { + Register(); + this->params_ = std::shared_ptr(new ParamsType()); + this->pmap_.insert({get_id_static(), this->params_}); +} + +template +TriangleCount::TriangleCount( + const TriangleCount &d) { + Register(); + this->params_ = d.params_; + this->pmap_ = d.pmap_; +} + +template +TriangleCount::TriangleCount( + const std::shared_ptr r) { + Register(); + this->params_ = r; + this->pmap_[get_id_static()] = r; +} + +template +TriangleCount::~TriangleCount() = default; + +template +void TriangleCount::Register() { + this->RegisterFunction( + {format::CSR::get_id_static()}, + GetTriangleCountCSR); +} + +template +std::vector +TriangleCount::get_sub_ids() { + return {typeid(TriangleCount)}; +} + +template +std::vector +TriangleCount::get_subs() { + return {new TriangleCount(*this)}; +} + +template +std::type_index TriangleCount::get_id_static() { + return typeid(TriangleCount); +} + +template +std::unordered_map +TriangleCount::Extract( + format::Format *format, std::vector c, + bool convert_input) { + return {{this->get_id(), std::forward( + GetTriangleCount(format, c, convert_input))}}; +}; + +template +int64_t *TriangleCount::GetTriangleCount( + format::Format *format, std::vector c, + bool convert_input) { + return this->Execute(this->params_.get(), c, convert_input, format); +} + +template +std::tuple>, int64_t *> +TriangleCount::GetTriangleCountCached( + format::Format *format, std::vector c, + bool convert_input) { + return this->CachedExecute(this->params_.get(), c, convert_input, false, + format); +} + +template +format::CSR *TransposeCSR( + format::CSR *csr) { + IDType n1 = csr->get_dimensions()[0], n2 = csr->get_dimensions()[1]; + NNZType m = csr->get_num_nnz(); + IDType *indegree = new IDType[n2]; + for (int i = 0; i < n2; ++i) indegree[i] = 0; + NNZType *row_ptr = csr->get_row_ptr(); + IDType *col = csr->get_col(); + ValueType *val = csr->get_vals(); + for (int i = 0; i < n1; ++i) { + for (int j = row_ptr[i]; j < row_ptr[i + 1]; ++j) { + ++indegree[col[j]]; + } + } + + int t_n1 = n2, t_n2 = n1; + NNZType *t_row_ptr = new NNZType[t_n1 + 1]; + t_row_ptr[0] = 0; + for (int i = 0; i < t_n1; ++i) { + t_row_ptr[i + 1] = indegree[i] + t_row_ptr[i]; + indegree[i] = 0; + } + + IDType *t_col = new IDType[m]; + ValueType *t_val = nullptr; + + if constexpr (!std::is_same_v) { + t_val = new ValueType[m]; + } + for (int i = 0; i < n1; ++i) { + for (int j = row_ptr[i]; j < row_ptr[i + 1]; ++j) { + t_col[t_row_ptr[col[j]] + indegree[col[j]]++] = i; + if constexpr (!std::is_same_v) { + t_val[t_row_ptr[col[j]] + indegree[col[j]] - 1] = val[j]; + } + } + } + + delete[] indegree; + return new format::CSR( + t_n1, m, t_row_ptr, t_col, t_val, format::kOwned, true); +} + +template +int64_t DirectedTriangleCount(format::CSR *csr) { + int n = csr->get_dimensions()[0]; + NNZType *row_ptr = csr->get_row_ptr(); + IDType *col = csr->get_col(); + + auto t_csr = TransposeCSR(csr); + auto t_row_ptr = t_csr->get_row_ptr(); + auto t_col = t_csr->get_col(); + + int *incoming = new int[n]; + for (int i = 0; i < n; ++i) { + incoming[i] = 0; + } + + int64_t triangleCount = 0; + for (int node = 0; node < n; ++node) { + for (int i = t_row_ptr[node]; i < t_row_ptr[node + 1]; ++i) { + incoming[t_col[i]] = node; // t_col[i] -> node + } + + for (int i = row_ptr[node]; i < row_ptr[node + 1]; ++i) { + int neig = col[i]; + if (node < neig) { + for (int j = row_ptr[neig]; j < row_ptr[neig + 1]; ++j) { + if (node < col[j] && incoming[col[j]]) // node -> neig -> col[j] -> node + ++triangleCount; + } + } + } + } + + delete[] incoming; + return triangleCount; +} + +template +int64_t UndirectedTriangleCount(format::CSR *csr) { + int n = csr->get_dimensions()[0]; + NNZType *row_ptr = csr->get_row_ptr(); + IDType *col = csr->get_col(); + + int *isConnected = new int[n]; + for (int i = 0; i < n; ++i) { + isConnected[i] = 0; + } + + int64_t triangleCount = 0; + for (int node = 0; node < n; ++node) { + for (int i = row_ptr[node]; i < row_ptr[node + 1]; ++i) { + isConnected[col[i]] = node; // col[i] <-> node + } + + for (int i = row_ptr[node]; i < row_ptr[node + 1]; ++i) { + int neig = col[i]; + if (node < neig) { + for (int j = row_ptr[neig]; j < row_ptr[neig + 1]; ++j) { + if (neig < col[j] && isConnected[col[j]]) // node, neig, col[j] + ++triangleCount; + } + } + } + } + delete[] isConnected; + return triangleCount; +} + +template +int64_t *TriangleCount::GetTriangleCountCSR( + std::vector formats, utils::Parameters *params) { + TriangleCountParams *param = static_cast(params); + bool countDirected = param->countDirected; + auto csr = formats[0]->AsAbsolute>(); + + int64_t triangleCount = 0; + if (countDirected) { + triangleCount = DirectedTriangleCount(csr); + } else { + triangleCount = UndirectedTriangleCount(csr); + } + + return new int64_t(triangleCount); +} + +#if !defined(_HEADER_ONLY) +#include "init/triangle_count.inc" +#endif +} // namespace sparsebase::feature \ No newline at end of file diff --git a/src/sparsebase/feature/triangle_count.h b/src/sparsebase/feature/triangle_count.h new file mode 100644 index 00000000..267430be --- /dev/null +++ b/src/sparsebase/feature/triangle_count.h @@ -0,0 +1,73 @@ +#include + +#include "sparsebase/config.h" +#include "sparsebase/feature/feature_preprocess_type.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/utils/parameterizable.h" + +#ifndef SPARSEBASE_PROJECT_TRIANGLE_COUNT_H +#define SPARSEBASE_PROJECT_TRIANGLE_COUNT_H +namespace sparsebase::feature { + +struct TriangleCountParams : utils::Parameters { + bool countDirected = 0; + TriangleCountParams() {} + TriangleCountParams(bool countDirected) : countDirected(countDirected){} +}; + +//! Find the triangle count in the graph representation of a format object +template +class TriangleCount : public feature::FeaturePreprocessType { + public: + //! An empty struct used for the parameters of Triangle Count + typedef TriangleCountParams ParamsType; + TriangleCount(); + TriangleCount(ParamsType); + TriangleCount(const TriangleCount &d); + TriangleCount(std::shared_ptr); + std::unordered_map Extract( + format::Format *format, std::vector, + bool convert_input) override; + std::vector get_sub_ids() override; + std::vector get_subs() override; + static std::type_index get_id_static(); + + //! Triangle Count executor function that carries out function matching + /*! + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting features. + * @param convert_input whether or not to convert the input format if that is needed. + * @return triangle count in the graph representation of `format` + */ + int64_t *GetTriangleCount(format::Format *format, std::vector contexts, + bool convert_input); + + std::tuple>, int64_t *> + //! Triangle Count executor function that carries out function atching with cached output + /*! + * @param format a single format pointer to any format + * @param contexts vector of contexts that can be used for extracting features. + * @param convert_input whether or not to convert the input format if that is needed. + * @return triangle count in the graph representation of `format` + */ + GetTriangleCountCached(format::Format *format,std::vector contexts, + bool convert_input); + //! Triangle Count implementation function for CSRs + /*! + * @param formats A vector containing a single format pointer that should point at a CSR object + * @param params a Parameters pointer, though it is not used in the function + * @return triangle count in the graph representations of 'format[0]' + */ + static int64_t *GetTriangleCountCSR(std::vector formats, + utils::Parameters *params); + ~TriangleCount(); + + protected: + void Register(); +}; + +} // namespace sparsebase::feature +#ifdef _HEADER_ONLY +#include "sparsebase/feature/triangle_count.cc" +#endif +#endif // SPARSEBASE_PROJECT_TRIANGLE_COUNT_H diff --git a/src/sparsebase/io/metis_graph_reader.cc b/src/sparsebase/io/metis_graph_reader.cc new file mode 100644 index 00000000..62a09ea2 --- /dev/null +++ b/src/sparsebase/io/metis_graph_reader.cc @@ -0,0 +1,113 @@ +#include +#include +#include "sparsebase/io/metis_graph_reader.h" +#include "sparsebase/config.h" +#include "sparsebase/io/reader.h" + +namespace sparsebase::io { + +template +MetisGraphReader::MetisGraphReader( + std::string filename, bool convert_to_zero_index) + : filename_(filename), + convert_to_zero_index_(convert_to_zero_index) {} + +template +sparsebase::object::Graph + *MetisGraphReader::ReadGraph() const { + IDType n, m; + IDType *row; + IDType *col; + int FMT = 0, NCON = 0; + bool isEdgeWeighted = false, isVertexWeighted = false; + std::ifstream infile(this->filename_); + std::string line; + if (infile.is_open()) { + while (std::getline(infile, line)) { + if (line[0] != '%') { + std::istringstream iss(line); + iss >> n >> m; + m *= 2; + n += (!convert_to_zero_index_); + if (iss >> FMT); + if (iss >> NCON); + if ((FMT == 1 || FMT == 11) && NCON == 0) NCON = 1; + isEdgeWeighted = (FMT == 1 || FMT == 11); + isVertexWeighted = (FMT >= 10 && NCON > 0); + break; + } + } + row = new IDType[m]; + col = new IDType[m]; + IDType node = (0 - convert_to_zero_index_), neig, curr = 0; + if constexpr (std::is_same_v) { + //No vertex/edge weights will be stored + int ignore; + while (std::getline(infile, line)) { + if (line[0] != '%') { + std::istringstream iss(line); + ++node; + if (isVertexWeighted) + for (int i = 0; i < NCON; ++i) + iss >> ignore; + while (iss >> neig) { + row[curr] = node; + col[curr] = (neig - convert_to_zero_index_); + if (isEdgeWeighted) + iss >> ignore; + ++curr; + } + } + } + return new sparsebase::object::Graph( + new format::COO(n, n, m, row, col, nullptr) + ); + } else { + ValueType* val = nullptr; + format::Array** vertexWeights = nullptr; + if (isVertexWeighted) { + vertexWeights = new format::Array*[n]; + ValueType* tmp = new ValueType[NCON]; + for (int i = 0; i < NCON; ++i) tmp[i] = 0; + if (!convert_to_zero_index_) + vertexWeights[0] = new format::Array(NCON, tmp); + } + if (isEdgeWeighted) { + val = new ValueType[m]; + } + while (std::getline(infile, line)) { + if (line[0] != '%') { + std::istringstream iss(line); + ++node; + if (isVertexWeighted) { + ValueType* tmp = new ValueType[NCON]; + for (int i = 0; i < NCON; ++i) iss >> tmp[i]; + vertexWeights[node] = new format::Array(NCON, tmp); + } + while (iss >> neig) { + row[curr] = node; + col[curr] = (neig - convert_to_zero_index_); + if (isEdgeWeighted) + { + iss >> val[curr]; + } + ++curr; + } + } + } + return new sparsebase::object::Graph( + new format::COO(n, n, m, row, col,val), + NCON, vertexWeights + ); + } + }else { + throw utils::ReaderException("file does not exist!"); + } +} + +template +MetisGraphReader::~MetisGraphReader(){}; +#ifndef _HEADER_ONLY +#include "init/metis_graph_reader.inc" +#endif +} // namespace sparsebase::io diff --git a/src/sparsebase/io/metis_graph_reader.h b/src/sparsebase/io/metis_graph_reader.h new file mode 100644 index 00000000..35c973b1 --- /dev/null +++ b/src/sparsebase/io/metis_graph_reader.h @@ -0,0 +1,44 @@ +#ifndef SPARSEBASE_PROJECT_METIS_GRAPH_READER_H +#define SPARSEBASE_PROJECT_METIS_GRAPH_READER_H +#include + +#include "sparsebase/config.h" +#include "sparsebase/io/reader.h" +#include "sparsebase/object/object.h" + +//! Interface for readers that can return a Graph instance +template +class ReadsGraph { + public: + //! Reads the file to a Graph instance and returns a pointer to it + virtual sparsebase::object::Graph *ReadGraph() const = 0; +}; + +namespace sparsebase::io { +//! Reader for the METIS GRAPH file format +/*! + * File format: https://people.sc.fsu.edu/~jburkardt/data/metis_graph/metis_graph.html + */ +template +class MetisGraphReader : public Reader, + public ReadsGraph { + public: + /*! + * Constructor for the MetisGraphReader class + * @param filename path to the file to be read + * @param convert_to_zero_index if set to true the indices will be converted + */ + explicit MetisGraphReader(std::string filename, bool convert_to_zero_index = false); + sparsebase::object::Graph *ReadGraph() const override; + ~MetisGraphReader() override; + + private: + std::string filename_; + bool convert_to_zero_index_; +}; + +} // namespace sparsebase::io +#ifdef _HEADER_ONLY +#include "metis_graph_reader.cc" +#endif +#endif // SPARSEBASE_PROJECT_METIS_GRAPH_READER_H diff --git a/src/sparsebase/io/metis_graph_writer.cc b/src/sparsebase/io/metis_graph_writer.cc new file mode 100644 index 00000000..43484cdb --- /dev/null +++ b/src/sparsebase/io/metis_graph_writer.cc @@ -0,0 +1,90 @@ +#include "sparsebase/io/metis_graph_writer.h" + +#include + +#include "sparsebase/config.h" +#include "sparsebase/io/sparse_file_format.h" +#include "sparsebase/io/writer.h" +#include "sparsebase/context/cpu_context.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/format/csr.h" + +namespace sparsebase::io { + +template +MetisGraphWriter::MetisGraphWriter( + std::string filename, bool edgeWeighted, bool vertexWeighted, + bool zero_indexed) + : filename_(filename), + edgeWeighted_(edgeWeighted), + vertexWeighted_(vertexWeighted), + zero_indexed_(zero_indexed) {} + +template +void MetisGraphWriter::WriteGraph( + object::Graph *graph) const {std::ofstream metisGraphFile; + metisGraphFile.open(filename_); + + format::Format* con = graph->get_connectivity(); + format::COO* coo = con->AsAbsolute>(); + converter::ConverterOrderTwo converterObj; + context::CPUContext cpu_context; + + format::CSR* csr = converterObj.template Convert>( + coo, &cpu_context); + auto n = (csr->get_dimensions()[0] - !zero_indexed_); + auto m = csr->get_num_nnz() / 2; + auto row_ptr = csr->get_row_ptr(); + auto col = csr->get_col(); + int NCON = 0; + std::string FMT = "0"; + metisGraphFile << " " << n << " " << m; + if constexpr (std::is_same_v) { + metisGraphFile << "\n"; + + for (int i = (1 - zero_indexed_); i < csr->get_dimensions()[0]; ++i) { + for (int j = row_ptr[i]; j < row_ptr[i + 1]; ++j) { + metisGraphFile << " " << (col[j] + zero_indexed_) << (j+1 == row_ptr[i+1] ? "" : " "); + } + metisGraphFile << "\n"; + } + } + else { + auto val = csr->get_vals(); + auto vertexWeights = graph->vertexWeights_; + if (vertexWeighted_) NCON = graph->ncon_; + if (edgeWeighted_ && !vertexWeighted_) FMT = "1"; + else if (edgeWeighted_) FMT = "11"; + else FMT = "10"; + if (NCON > 0 || FMT != "0") { + metisGraphFile << " " << FMT; + if (NCON > 0) + metisGraphFile << " " << NCON; + } + metisGraphFile << "\n"; + + for (int i = (1 - zero_indexed_); i < csr->get_dimensions()[0]; ++i) { + if (vertexWeighted_) + { + auto weights = vertexWeights[i]->get_vals(); + for (int j = 0; j < NCON; ++j) + metisGraphFile << weights[j] << " "; + metisGraphFile << " "; + } + for (int j = row_ptr[i]; j < row_ptr[i + 1]; ++j) { + metisGraphFile << " " << (col[j] + zero_indexed_); + if (edgeWeighted_) + metisGraphFile << " " << val[j] << (j+1 == row_ptr[i+1] ? "" : " "); + if (j+1 != row_ptr[i+1]) + metisGraphFile << " "; + } + metisGraphFile << "\n"; + } + } + metisGraphFile.close(); +} + +#ifndef _HEADER_ONLY +#include "init/metis_graph_writer.inc" +#endif +} // namespace sparsebase::io diff --git a/src/sparsebase/io/metis_graph_writer.h b/src/sparsebase/io/metis_graph_writer.h new file mode 100644 index 00000000..44e48bda --- /dev/null +++ b/src/sparsebase/io/metis_graph_writer.h @@ -0,0 +1,41 @@ +#ifndef SPARSEBASE_PROJECT_METIS_GRAPH_WRITER_H +#define SPARSEBASE_PROJECT_METIS_GRAPH_WRITER_H +#include +#include "sparsebase/config.h" +#include "sparsebase/io/writer.h" +#include "sparsebase/object/object.h" + +namespace sparsebase::io { + +//! Writer for the METIS GRAPH file format +/*! + * File format: https://people.sc.fsu.edu/~jburkardt/data/metis_graph/metis_graph.html + */ +template +class MetisGraphWriter : public Writer, + public WritesGraph { + public: + /*! + * Constructor for the MetisGraphWriter class + * @param filename to be written + * @param edgeWeighted set to true if edge weights exists and wanted in output + * @param vertexWeighted set to true if vertex weights exists and wanted in output + * @param zero_indexed set to true if given graph vertices are 0 indexed + */ + explicit MetisGraphWriter(std::string filename, + bool edgeWeighted = false, bool vertexWeighted = false, + bool zero_indexed = false); + ~MetisGraphWriter() override = default; + void WriteGraph(object::Graph *graph) const override; + private: + std::string filename_; + bool edgeWeighted_; + bool vertexWeighted_; + bool zero_indexed_; +}; + +} // namespace sparsebase::io +#ifdef _HEADER_ONLY +#include "metis_graph_writer.cc" +#endif +#endif // SPARSEBASE_PROJECT_METIS_GRAPH_WRITER_H diff --git a/src/sparsebase/io/mtx_reader.cc b/src/sparsebase/io/mtx_reader.cc index 52e64713..437dfb21 100644 --- a/src/sparsebase/io/mtx_reader.cc +++ b/src/sparsebase/io/mtx_reader.cc @@ -140,8 +140,8 @@ format::COO fin >> w; if (w != 0) { - long_cols[num_nnz] = l / N; - long_rows[num_nnz] = l % N; + long_cols[num_nnz] = l / M; + long_rows[num_nnz] = l % M; if constexpr (weighted) long_vals[num_nnz] = w; num_nnz++; } @@ -166,6 +166,10 @@ format::COO *MTXReader::ReadCOO() const { bool weighted = options_.field != MTXFieldOptions::pattern; if (options_.format == MTXFormatOptions::array) { + if (options_.symmetry != MTXSymmetryOptions::general){ + throw utils::ReaderException( + "Library does not support reading array files that are symmetric, skew-symmetric, or hermetian"); + } if (weighted) { if constexpr (!std::is_same_v) { return this->ReadArrayIntoCOO(); @@ -375,12 +379,17 @@ format::COO check_diagonal = false; else check_diagonal = true; - if (check_diagonal && m != n) { + if (!check_diagonal || m != n) { row[actual_nnzs] = n; col[actual_nnzs] = m; if constexpr (weighted && !std::is_same_v && - weighted) - vals[actual_nnzs] = vals[actual_nnzs - 1]; + weighted) { + if constexpr (symm == (int)MTXSymmetryOptions::skew_symmetric) { + vals[actual_nnzs] = -vals[actual_nnzs - 1]; + } else { + vals[actual_nnzs] = vals[actual_nnzs - 1]; + } + } actual_nnzs++; } } diff --git a/src/sparsebase/io/mtx_writer.cc b/src/sparsebase/io/mtx_writer.cc new file mode 100644 index 00000000..3fbbf0b2 --- /dev/null +++ b/src/sparsebase/io/mtx_writer.cc @@ -0,0 +1,429 @@ +#include "sparsebase/io/mtx_writer.h" + +#include + +#include "sparsebase/config.h" +#include "sparsebase/io/sparse_file_format.h" +#include "sparsebase/io/writer.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/utils/logger.h" + +namespace sparsebase::io { + +template +MTXWriter::MTXWriter( + std::string filename, std::string object, std::string format, std::string field, std::string symmetry) + : filename_(filename), + object_(object), + format_(format), + field_(field), + symmetry_(symmetry) {} + + +// This function does: +// 1- Take input matrix as COO +// 2- Convert it into MTX format +// 3-Write it as .mtx file +template +void MTXWriter::WriteCOO( + format::COO *coo) const { + + + + //information about MTX (Matrix Market Format) https://math.nist.gov/MatrixMarket/formats.html and https://networkrepository.com/mtx-matrix-market-format.html + + //Matrix market format has some header parameters, following if conditions check these input parameters and + //throws exception if its wrong, or illegal combination with other parameters or its not supported with sparsebase + if (object_ != "matrix" && object_ != "vector") + { + throw utils::WriterException("Illegal value for the 'object' option in matrix market header"); + } + else if (object_ == "vector") + { + throw utils::WriterException("Matrix market writer does not currently support writing vectors."); + } + if (format_ != "array" && format_ != "coordinate") + { + throw utils::WriterException("Illegal value for the 'format' option in matrix market header"); + } + if (field_ != "real" && field_ != "double" && field_ != "complex" && field_ != "integer" && field_ != "pattern") + { + throw utils::WriterException("Illegal value for the 'field' option in matrix market header"); + } + if (symmetry_ != "general" && symmetry_ != "symmetric" && symmetry_ != "skew-symmetric" && symmetry_ != "hermitian") + { + throw utils::WriterException("Illegal value for the 'symmetry' option in matrix market header"); + } + if (format_ == "array" && field_ == "pattern") + { + throw utils::WriterException("Matrix market files with array format cannot have the field ""'pattern' "); + } + if (format_ == "array" && symmetry_ != "general") + { + throw utils::WriterException("Matrix market files with array format cannot have the property 'symmetry' "); + } + if (symmetry_ == "hermitian") + { + throw utils::WriterException("Matrix market writer does not currently support hermitian symmetry."); + } + + // open a mtx file to desired filename + std::ofstream mtxFile; + mtxFile.open(filename_); + + //insert the mtx headers which are inputted from user + mtxFile << "%%MatrixMarket " << object_ << " " << format_ << " " << field_ << " " << symmetry_ << "\n"; + + //TODO add warning when given field and actual data format is not same (integer vs float etc) + + //special condition to check if value type of matrix is void or not, MTX format supports void values if it has pattern parameter + if constexpr (std::is_same_v) + { + if (field_ != "pattern") + throw utils::WriterException("Cannot write an MTX with void ValueType, unless field is pattern."); + } + + //consruct dimensions of the matrix + auto dimensions = coo->get_dimensions(); + IDType* row = coo->get_row(); + IDType* col = coo->get_col(); + ValueType* val = coo->get_vals(); + + //when pattern is selected no need to give values + if (val != NULL && field_ == "pattern") + { + utils::Logger logger(typeid(this)); + logger.Log("Pattern selected but values given.", utils::LOG_LVL_WARNING); + } + + //check matrix Symmetry, user inputs this as parameter but we check it anyways to detect if any user error happens. + //also if skew symmetric or hermitian symmetric is choosen we change the matrix accordingly here before writing to file. + bool saidSymmetric = (symmetry_ == "symmetric" || symmetry_ == "skew-symmetric" || symmetry_ == "hermitian"); + int NNZ = coo->get_num_nnz(); + int count_symmetric = 0; + int count_diagonal = 0; + int is_diagonal_all_zero = true; + //check symmetry + //saidSymmetric: user said symmetric (but it might not be) + if (saidSymmetric == true && dimensions[0] != dimensions[1]) + { + throw utils::WriterException("Matrix is not symmetric!"); + } + //easier symmetry check first, it needs to be square to be symmetric + if (saidSymmetric && dimensions[0] == dimensions[1]) + { + for (int i = 0; i < coo->get_num_nnz(); ++i) + { + //checking every non diagonal entry and their symmetric coordinate and compare values + if (row[i] != col[i]) + { // Non-diagonal entry, check symmetric counterpart + bool found_symmetric = false; + for (int j = 0; j < coo->get_num_nnz(); ++j) + { + if (symmetry_ == "skew-symmetric") + { + //thse is_same_v checks are to ensure function works even if value type is void becuase of pattern parameter + if constexpr (std::is_same_v) + { + break; //pattern cannot be skew-symmetric + } + else + { + if (row[j] == col[i] && col[j] == row[i] && val[j] == -val[i]) + { + found_symmetric = true; + count_symmetric++; + break; + } + } + } + else + { + //thse is_same_v checks are to ensure function works even if value type is void becuase of pattern parameter + if constexpr (std::is_same_v) + { + if (row[j] == col[i] && col[j] == row[i]) + { + found_symmetric = true; + count_symmetric++; + break; + } + } + else + { + if (row[j] == col[i] && col[j] == row[i] && val[j] == val[i]) + { + found_symmetric = true; + count_symmetric++; + break; + } + } + } + } + //found_symmetric is true if matrix is actually symmetric (user might said not symmetric) + if (!found_symmetric) { + throw utils::WriterException("Matrix is not symmetric!"); + } + } + else //diagonal + { + count_diagonal++; + //thse is_same_v checks are to ensure function works even if value type is void becuase of pattern parameter + if constexpr (std::is_same_v) + { + //zero + } + else + { + if (val[i] != 0) + { + //checking this because we need this for skew-symmetry check + is_diagonal_all_zero = false; + } + } + } + } + //check if matrix is actually skew-symmetric + if(symmetry_ == "skew-symmetric") + { + if (!is_diagonal_all_zero) + { + throw utils::WriterException("Skew-symmetric matrix with non-zero diagonal values!"); + } + NNZ -= (count_symmetric/2) + count_diagonal; + } + else + { + NNZ -= (count_symmetric/2); + } + } + + //dimensions and nnz + //if array is given rather than coordinate, dimension format of the file is different, so it is handled here + if (format_ == "array") + { + mtxFile << dimensions[0] << " " << dimensions[1] << "\n"; + } + else //cordinate + { + mtxFile << dimensions[0] << " " << dimensions[1] << " " << NNZ << "\n"; + } + //data lines are written here for array + if (format_ == "array") + { + //write data lines + if constexpr (std::is_same_v) + { + int index = 0; + while (index < dimensions[0]*dimensions[1]) + { + mtxFile << 0 << "\n"; + index++; + } + } + else + { + //sort according to column values + std::vector> sort_vec; + for (int i = 0; i < coo->get_num_nnz(); i++) { + sort_vec.emplace_back(col[i], row[i], val[i]); + } + std::sort(sort_vec.begin(), sort_vec.end(), + [](std::tuple t1, std::tuple t2) { + if (std::get<0>(t1) == std::get<0>(t2)) { + return std::get<1>(t1) < std::get<1>(t2); + } + return std::get<0>(t1) < std::get<0>(t2); + }); + + // Write the coo matrix in array format + int index = 0; + int current_index = 0; + for (int i = 0; i < coo->get_num_nnz(); i++) + { + current_index = std::get<0>(sort_vec[i]) * dimensions[0] + std::get<1>(sort_vec[i]); + while (index < current_index) + { + mtxFile << 0 << "\n"; + index++; + } + mtxFile << std::get<2>(sort_vec[i]) << "\n"; + index++; + } + while (index < dimensions[0]*dimensions[1]) + { + mtxFile << 0 << "\n"; + index++; + } + } + } + //data lines for coordinate are written here + else //coordinate + { + + if (saidSymmetric) + { + for (int i = 0; i < coo->get_num_nnz(); i++) + { + if (symmetry_ != "skew-symmetric" && col[i] == row[i]) + {//on diagonal entries + mtxFile << row[i]+1 << " " << col[i]+1; + //thse is_same_v checks are to ensure function works even if value type is void becuase of pattern parameter + if constexpr (std::is_same_v) + { + if (field_ == "pattern") + mtxFile << "\n"; + } + else + { + if (field_ == "pattern") + { + mtxFile << "\n"; + } + else + { + mtxFile << " " << val[i] << "\n"; + } + } + } + if (col[i] < row[i]) + {//strictly below diagonal entries + mtxFile << row[i]+1 << " " << col[i]+1; + //thse is_same_v checks are to ensure function works even if value type is void becuase of pattern parameter + if constexpr (std::is_same_v) + { + if (field_ == "pattern") + mtxFile << "\n"; + } + else + { + if (field_ == "pattern") + { + mtxFile << "\n"; + } + else + { + mtxFile << " " << val[i] << "\n"; + } + } + } + } + } + //data lines for general format is written here + else //general + { + for (int i = 0; i < coo->get_num_nnz(); i++) + { + mtxFile << row[i]+1 << " " << col[i]+1; + //thse is_same_v checks are to ensure function works even if value type is void becuase of pattern parameter + if constexpr (std::is_same_v) + { + if (field_ == "pattern") + mtxFile << "\n"; + } + else + { + if (field_ == "pattern") + { + mtxFile << "\n"; + } + else + { + mtxFile << " " << val[i] << "\n"; + } + } + } + } + } + mtxFile.close(); +} + +template +void MTXWriter::WriteCSR( + format::CSR *csr) const { + //this is for writing mtx file from CSR rather than COO + //this function doesnt convert CSR to MTX, it rather converts CSR to COO using sparsebase conversion + //then calls above WriteCOO function with this converted COO + converter::ConverterOrderTwo converterObj; + context::CPUContext cpu_context; + auto coo = converterObj.template Convert>( + csr, &cpu_context); + WriteCOO(coo); +} + +template +void MTXWriter::WriteArray( + format::Array *arr) const { + //this function takes array as input and writes it strictly as array format + //it is same as WriteCOO otherwise + + //illegal parameter checks + if (object_ != "matrix" && object_ != "vector") + { + throw utils::WriterException("Illegal value for the 'object' option in matrix market header"); + } + else if (object_ == "vector") + { + throw utils::WriterException("Matrix market writer does not currently support writing vectors."); + } + if (format_ != "array" && format_ != "coordinate") + { + throw utils::WriterException("Illegal value for the 'format' option in matrix market header"); + } + if (field_ != "real" && field_ != "double" && field_ != "complex" && field_ != "integer" && field_ != "pattern") + { + throw utils::WriterException("Illegal value for the 'field' option in matrix market header"); + } + if (symmetry_ != "general" && symmetry_ != "symmetric" && symmetry_ != "skew-symmetric" && symmetry_ != "hermitian") + { + throw utils::WriterException("Illegal value for the 'symmetry' option in matrix market header"); + } + if (format_ == "array" && field_ == "pattern") + { + throw utils::WriterException("Matrix market files with array format cannot have the field ""'pattern' "); + } + if (format_ == "array" && symmetry_ != "general") + { + throw utils::WriterException("Matrix market files with array format cannot have the property 'symmetry' "); + } + if (symmetry_ == "hermitian") + { + throw utils::WriterException("Matrix market writer does not currently support hermitian symmetry."); + } + if (format_ == "coordinate") + { + throw utils::WriterException("Matrix market writer does not currently support writing array as coordinate."); + } + + std::ofstream mtxFile; + mtxFile.open(filename_); + + //header + mtxFile << "%%MatrixMarket " << object_ << " " << format_ << " " << field_ << " " << symmetry_ << "\n"; + + //TODO add warning when given field and actual data format is not same (integer vs float etc) + + if constexpr (std::is_same_v) + { + throw utils::WriterException("Cannot write an MTX with void ValueType"); + } + else + { + ValueType* val = arr->get_vals(); + auto dimensions = arr->get_dimensions(); + //dimensions + mtxFile << 1 << " " << dimensions[0] << "\n"; + + for (int i = 0; i < dimensions[0]; i++) + { + mtxFile << val[i] << "\n"; + } + mtxFile.close(); + } + } + +#ifndef _HEADER_ONLY +#include "init/mtx_writer.inc" +#endif +} // namespace sparsebase::io \ No newline at end of file diff --git a/src/sparsebase/io/mtx_writer.h b/src/sparsebase/io/mtx_writer.h new file mode 100644 index 00000000..30dd2f4c --- /dev/null +++ b/src/sparsebase/io/mtx_writer.h @@ -0,0 +1,45 @@ +#ifndef SPARSEBASE_PROJECT_MTX_WRITER_H +#define SPARSEBASE_PROJECT_MTX_WRITER_H +#include +#include "sparsebase/config.h" +#include "sparsebase/io/writer.h" + +namespace sparsebase::io { + +//! Writer for the Matrix Market File Format +/*! + * Detailed explanations of the MTX format can be found in these links: + * - https://networkrepository.com/mtx-matrix-market-format.html + * - https://math.nist.gov/MatrixMarket/formats.html + */ +template +class MTXWriter : public Writer, + public WritesCOO, + public WritesCSR, + public WritesArray { + public: + explicit MTXWriter( + std::string filename, + std::string object = "matrix", + std::string format = "coordinate", + std::string field = "real", + std::string symmetry = "general" + ); + ~MTXWriter() override = default; + void WriteCOO(format::COO *coo) const override; + void WriteCSR(format::CSR *csr) const override; + void WriteArray(format::Array *arr) const override; + + private: + std::string object_; + std::string filename_; + std::string format_; + std::string field_; + std::string symmetry_; +}; + +} // namespace sparsebase::io +#ifdef _HEADER_ONLY +#include "mtx_writer.cc" +#endif +#endif // SPARSEBASE_PROJECT_MTX_WRITER_H diff --git a/src/sparsebase/io/patoh_reader.cc b/src/sparsebase/io/patoh_reader.cc new file mode 100644 index 00000000..3262e937 --- /dev/null +++ b/src/sparsebase/io/patoh_reader.cc @@ -0,0 +1,248 @@ +#include "sparsebase/io/patoh_reader.h" + +#include +#include + +#include "sparsebase/config.h" + +namespace sparsebase::io{ + +template +PatohReader::PatohReader(std::string filename) + :filename_(filename){ + std::ifstream fin(filename_); + if(fin.is_open()){ + std::string header_line; + while (fin.peek() == '%') + fin.ignore(std::numeric_limits::max(), '\n'); // To skip the comment + std::getline(fin,header_line); // Information about hypergraphs + options_ = ParseHeader(header_line); + } + else{ + throw utils::ReaderException("Can not read HyperGraph\n"); + } + } + +template +typename PatohReader::HyperGraphOptions +PatohReader::ParseHeader( + std::string header_line) const { + std::stringstream line_ss(header_line); + HyperGraphOptions options; + std::string base_type, cell_num, net_num, pin_num,weighted_scheme,constraint_num; + line_ss >> base_type >> cell_num >> net_num >> pin_num>>weighted_scheme>>constraint_num; + options.base_type = stoi(base_type); + options.cell_num = stoi(cell_num); + options.net_num = stoi(net_num); + options.pin_num = stoi(pin_num); + if (weighted_scheme != "") + options.weighted_scheme = stoi(weighted_scheme); + if (constraint_num != "") + options.constraint_num = stoi(constraint_num); + return options; +} + +template +sparsebase::object::HyperGraph + *PatohReader::ReadHyperGraph() const{ + + std::ifstream fin(filename_); + //Ignore headers and comments: + IDType *pin_arr = new IDType[options_.pin_num]; + NNZType *xpin_arr = new NNZType[options_.net_num+1]; + IDType *net_arr = new IDType[options_.pin_num]; + NNZType *xnet_arr = new NNZType[options_.cell_num + 1]; + xpin_arr[0] = 0; + xnet_arr[0] = 0; + + //Weight arrays of cells and nets + //ValueType *cell_weight_arr = new ValueType[options_.cell_num]; + //std::fill(cell_weight_arr, cell_weight_arr + options_.cell_num, 1); + //ValueType *net_weight_arr = new ValueType[options_.net_num]; + //std::fill(net_weight_arr, net_weight_arr + options_.net_num, 1); + + // Null arrays for filling the val array of CSR Matrix + ValueType *xpin_val_arr = nullptr; + ValueType *xnet_val_arr = nullptr; + if constexpr (std::is_same_v) { + std::string line; + while (fin.peek() == '%') + fin.ignore(std::numeric_limits::max(), '\n'); // To skip the comment + std::getline(fin, line); // To skip Information about hypergraphs + int i =0,k =0; + while (std::getline(fin, line)) + { + std::stringstream ss(line); + //If line is not comment + if (line[0] != '%') { + int num; + int net_size = 0; + while (ss >> num) { + pin_arr[i] = num; + i++; + net_size++; + } + xpin_arr[k + 1] = xpin_arr[k] + net_size; + k++; + } + } + + if (options_.base_type == 0) { + int add_index = 0; + for (int m = 0; m < options_.cell_num; m++) + { + int vertice_size = 0; + for (int n = 0; n < options_.pin_num; n++) + { + if (m == pin_arr[n]) + { + int temp_index = 0; + while (xpin_arr[temp_index + 1] < n + 1) + { + temp_index++; + } + net_arr[add_index] = temp_index; + add_index++; + vertice_size++; + } + } + xnet_arr[m + 1] = xnet_arr[m] + vertice_size; + } + } + + else if (options_.base_type == 1) { // shift m by 1 + int add_index = 0; + for (int m = 0; m < options_.cell_num; m++) + { + int vertice_size = 0; + for (int n = 0; n < options_.pin_num; n++) + { + if (m+1 == pin_arr[n]) + { + int temp_index = 0; + while (xpin_arr[temp_index + 1] < n + 1) + { + temp_index++; + } + net_arr[add_index] = temp_index+1; + add_index++; + vertice_size++; + } + } + xnet_arr[m + 1] = xnet_arr[m] + vertice_size; + } + } + + return new sparsebase::object::HyperGraph( + new format::CSR(options_.net_num,options_.cell_num,xpin_arr,pin_arr,xpin_val_arr,sparsebase::format::kNotOwned,true), + options_.base_type, + options_.constraint_num, + new format::CSR(options_.cell_num,options_.net_num,xnet_arr,net_arr,xnet_val_arr,sparsebase::format::kNotOwned,true) + + ); + } + + else { + ValueType *cell_weight_arr = new ValueType[options_.cell_num]; + std::fill(cell_weight_arr, cell_weight_arr + options_.cell_num, 1); + ValueType *net_weight_arr = new ValueType[options_.net_num]; + std::fill(net_weight_arr, net_weight_arr + options_.net_num, 1); + std::string line; + while (fin.peek() == '%') + fin.ignore(std::numeric_limits::max(), '\n'); // To skip the comment + std::getline(fin, line); // To skip Information about hypergraphs + int i =0,k =0,l=0; + while (std::getline(fin, line)) + { + std::stringstream ss(line); + //If line is not comment + if (line[0] != '%') { + int num; + int net_size = 0; + if (options_.weighted_scheme == 2 || options_.weighted_scheme == 3) { + if (!(options_.weighted_scheme == 3 && fin.eof())) { + ss >> num; + net_weight_arr[k] = num; + } + } + while (ss >> num) { + if((options_.weighted_scheme == 1 || options_.weighted_scheme == 3)&& (fin.eof()))// The last line of the file carries to information of cell's weights + { + cell_weight_arr[l] = num; + l++; + } + else { + pin_arr[i] = num; + i++; + net_size++; + } + } + if(!((options_.weighted_scheme == 1 || options_.weighted_scheme == 3) && (fin.eof()))){ + xpin_arr[k + 1] = xpin_arr[k] + net_size; + k++; + } + } + } + + if (options_.base_type == 0) { + int add_index = 0; + for (int m = 0; m < options_.cell_num; m++) + { + int vertice_size = 0; + for (int n = 0; n < options_.pin_num; n++) + { + if (m == pin_arr[n]) + { + int temp_index = 0; + while (xpin_arr[temp_index + 1] < n + 1) + { + temp_index++; + } + net_arr[add_index] = temp_index; + add_index++; + vertice_size++; + } + } + xnet_arr[m + 1] = xnet_arr[m] + vertice_size; + } + } + + else if (options_.base_type == 1) { // shift m by 1 + int add_index = 0; + for (int m = 0; m < options_.cell_num; m++) + { + int vertice_size = 0; + for (int n = 0; n < options_.pin_num; n++) + { + if (m+1 == pin_arr[n]) + { + int temp_index = 0; + while (xpin_arr[temp_index + 1] < n + 1) + { + temp_index++; + } + net_arr[add_index] = temp_index+1; + add_index++; + vertice_size++; + } + } + xnet_arr[m + 1] = xnet_arr[m] + vertice_size; + } + } + + return new sparsebase::object::HyperGraph( + new format::CSR(options_.net_num,options_.cell_num,xpin_arr,pin_arr,xpin_val_arr,sparsebase::format::kNotOwned,true), + new format::Array(options_.net_num,net_weight_arr), + new format::Array(options_.cell_num,cell_weight_arr), + options_.base_type, + options_.constraint_num, + new format::CSR(options_.cell_num,options_.net_num,xnet_arr,net_arr,xnet_val_arr,sparsebase::format::kNotOwned,true) + ); + } +} +template +PatohReader::~PatohReader(){}; +#ifndef _HEADER_ONLY +#include "init/patoh_reader.inc" +#endif +} \ No newline at end of file diff --git a/src/sparsebase/io/patoh_reader.h b/src/sparsebase/io/patoh_reader.h new file mode 100644 index 00000000..1bab76a2 --- /dev/null +++ b/src/sparsebase/io/patoh_reader.h @@ -0,0 +1,44 @@ +#ifndef SPARSEBASE_PROJECT_PATOH_READER_H +#define SPARSEBASE_PROJECT_PATOH_READER_H +#include +#include "sparsebase/config.h" +#include "sparsebase/io/reader.h" +#include "sparsebase/object/object.h" + +template +class ReadsHyperGraph { + public: + //! Reads the file to a Graph instance and returns a pointer to it + virtual sparsebase::object::HyperGraph *ReadHyperGraph() const = 0; +}; + +namespace sparsebase::io { + +template +class PatohReader : public Reader, + public ReadsHyperGraph{ + +public : + +explicit PatohReader(std::string filename); +sparsebase::object::HyperGraph *ReadHyperGraph() const override; +~PatohReader() override; + +private: +struct HyperGraphOptions { + IDType base_type; + IDType cell_num; + IDType net_num; + IDType pin_num; + IDType weighted_scheme = 0; // Optional, If it equals 1 cells are weighted, If it equals 2 nets are weighted, if equals 3, both are weighted + IDType constraint_num = 1; // Optional +}; +std::string filename_; +HyperGraphOptions options_; +HyperGraphOptions ParseHeader(std::string header_line) const; +}; +} +#ifdef _HEADER_ONLY +#include "patoh_reader.cc" +#endif +#endif // SPARSEBASE_PROJECT_PATOH_READER_H \ No newline at end of file diff --git a/src/sparsebase/io/patoh_writer.cc b/src/sparsebase/io/patoh_writer.cc new file mode 100644 index 00000000..ac2869a9 --- /dev/null +++ b/src/sparsebase/io/patoh_writer.cc @@ -0,0 +1,260 @@ +#include "sparsebase/io/patoh_writer.h" +#include +#include "sparsebase/config.h" +#include "sparsebase/io/sparse_file_format.h" +#include "sparsebase/io/writer.h" +#include "sparsebase/format/csr.h" + + + +namespace sparsebase::io { + +template +PatohWriter::PatohWriter( + std::string filename, bool is_zero_indexed, bool is_edge_weighted, bool is_vertex_weighted, int constraint_num) + : filename_(filename), + is_zero_indexed_(is_zero_indexed), + is_edge_weighted_(is_edge_weighted), + is_vertex_weighted_(is_vertex_weighted) {} + +template +void PatohWriter::WriteHyperGraph( + object::HyperGraph *hyperGraph) const { + + std::ofstream patohFile; + patohFile.open(filename_); + + format::Format* con = hyperGraph->get_connectivity(); + auto base_type = hyperGraph->base_type_; + auto constraint_num = hyperGraph->constraint_num_; + auto cell_num = con->get_dimensions()[1]; // Cell Num = Vertices num + auto net_num = hyperGraph->n_; + auto pin_num = hyperGraph->m_; + + auto xpin_arr = con->AsAbsolute>()->get_row_ptr(); + auto pin_arr = con->AsAbsolute>()->get_col(); + + auto xNetsCSR = hyperGraph->xNetCSR_; + auto xnet_arr = xNetsCSR->get_row_ptr(); + auto net_arr = xNetsCSR->get_col(); + + if constexpr (std::is_same_v){ + int index_num; + if(is_zero_indexed_){ + index_num = 0; + if(base_type == 1){ // If the hypergraph was 1 indexed convert it into 0 index + for (int i = 0; i < pin_num; i++) { + pin_arr[i] -= 1; + net_arr[i] -= 1; + } + } + } + + else if(!is_zero_indexed_){ + index_num = 1; + if(base_type == 0) + { + for (int i = 0; i < pin_num; i++) { + pin_arr[i] += 1; + net_arr[i] += 1; + } + } + } + + //Create Header Line + std::string header_line; + //header_line = std::to_string(index_num) + ' ' + std::to_string(cell_num) + ' ' + std::to_string(net_num) + ' ' + std::to_string(pin_num); + header_line = std::to_string(index_num) + ' ' + std::to_string(cell_num) + ' ' + std::to_string(net_num) + ' ' + std::to_string(pin_num); + patohFile << header_line; + patohFile << "\n"; + int j = 0; + int xpin_arr_size = 1; + while (j < pin_num) { + int xpin_arr_counter = 0; + std::string str_line = ""; + while (xpin_arr_counter < (xpin_arr[xpin_arr_size] - xpin_arr[xpin_arr_size - 1])) { + str_line += std::to_string(pin_arr[j]) + " "; + j++; + xpin_arr_counter++; + } + str_line.erase(str_line.size() - 1); + patohFile << str_line; + if (j != pin_num) + patohFile << "\n"; + xpin_arr_size++; + } + } + + else{ + auto cell_weights = hyperGraph->cellWeights_; + auto net_weights = hyperGraph->netWeights_; + auto cell_weight_arr = cell_weights->get_vals(); + auto net_weight_arr = net_weights->get_vals(); + + int index_num; + int j = 0; + int xpin_arr_size = 1; + if(is_zero_indexed_){ + index_num = 0; + if(base_type == 1){ // If the hypergraph was 1 indexed convert it into 0 index + for (int i = 0; i < pin_num; i++) { + pin_arr[i] -= 1; + net_arr[i] -= 1; + } + } + } + + else if(!is_zero_indexed_){ + index_num = 1; + if(base_type == 0) + { + for (int i = 0; i < pin_num; i++) { + pin_arr[i] += 1; + net_arr[i] += 1; + } + } + } + + if (!is_vertex_weighted_ && !is_edge_weighted_) { + //Create Header Line + std::string header_line; + //int index_num = 0; + header_line = std::to_string(index_num) + ' ' + std::to_string(cell_num) + ' ' + std::to_string(net_num) + ' ' + std::to_string(pin_num); + patohFile << header_line; + patohFile << "\n"; + + while (j < pin_num) { + int xpin_arr_counter = 0; + std::string str_line = ""; + while (xpin_arr_counter < (xpin_arr[xpin_arr_size] - xpin_arr[xpin_arr_size - 1])) { + str_line += std::to_string(pin_arr[j]) + " "; + j++; + xpin_arr_counter++; + } + str_line.erase(str_line.size() - 1); + patohFile << str_line; + if (j != pin_num) + patohFile << "\n"; + xpin_arr_size++; + + } + } + + else if (is_vertex_weighted_ && !is_edge_weighted_) { + + //Create Header Line + std::string header_line; + int weights_info = 1; // If only vertices are weighted + + header_line = std::to_string(index_num) + ' ' + std::to_string(cell_num) + ' ' + std::to_string(net_num) + ' ' + std::to_string(pin_num) + ' ' + std::to_string(weights_info); + if(constraint_num != 1){ // No need to add the constraint num to the header if its value is 1 + header_line += ' '+ std::to_string(constraint_num); + } + patohFile << header_line; + patohFile << "\n"; + + while (j < pin_num) { + int xpin_arr_counter = 0; + std::string str_line = ""; + while (xpin_arr_counter < (xpin_arr[xpin_arr_size] - xpin_arr[xpin_arr_size - 1])) { + str_line += std::to_string(pin_arr[j]) + " "; + j++; + xpin_arr_counter++; + } + str_line.erase(str_line.size() - 1); + patohFile << str_line; + patohFile << "\n"; + xpin_arr_size++; + + } + + // Insert cell weights + int cell_weight_index = 0; + std::string weight_line = ""; + while (cell_weight_index < cell_num) { + weight_line += std::to_string(cell_weight_arr[cell_weight_index]) + " "; + cell_weight_index++; + } + weight_line.erase(weight_line.size() - 1); + patohFile << weight_line; + } + + else if (!is_vertex_weighted_ && is_edge_weighted_) { + //Create Header Line + std::string header_line; + int net_weights_index = 0; + int weights_info = 2; // If only nets are weighted + + header_line = std::to_string(index_num) + ' ' + std::to_string(cell_num) + ' ' + std::to_string(net_num) + ' ' + std::to_string(pin_num) + ' ' + std::to_string(weights_info); + if(constraint_num != 1){ // No need to add the constraint num to the header if its value is 1 + header_line += ' '+ std::to_string(constraint_num); + } + patohFile << header_line; + patohFile << "\n"; + + while (j < pin_num) { + int xpin_arr_counter = 0; + std::string str_line = ""; + str_line += std::to_string(net_weight_arr[net_weights_index]) + " "; + while (xpin_arr_counter < (xpin_arr[xpin_arr_size] - xpin_arr[xpin_arr_size - 1])) { + str_line += std::to_string(pin_arr[j]) + " "; + j++; + xpin_arr_counter++; + } + str_line.erase(str_line.size() - 1); + patohFile << str_line; + if (j != pin_num) + patohFile << "\n"; + xpin_arr_size++; + net_weights_index++; + + } + } + + else if (is_vertex_weighted_ && is_edge_weighted_) { + //Create Header Line + std::string header_line; + //int index_num = 0; + int net_weights_index = 0; + int weights_info = 3; // Both Nets and Vertices are weighted + header_line = std::to_string(index_num) + ' ' + std::to_string(cell_num) + ' ' + std::to_string(net_num) + ' ' + std::to_string(pin_num) + ' ' + std::to_string(weights_info); + patohFile << header_line; + patohFile << "\n"; + + while (j < pin_num) { + int xpin_arr_counter = 0; + std::string str_line = ""; + str_line += std::to_string(net_weight_arr[net_weights_index]) + " "; + while (xpin_arr_counter < (xpin_arr[xpin_arr_size] - xpin_arr[xpin_arr_size - 1])) { + str_line += std::to_string(pin_arr[j]) + " "; + j++; + xpin_arr_counter++; + } + str_line.erase(str_line.size() - 1); + patohFile << str_line; + patohFile << "\n"; + xpin_arr_size++; + net_weights_index++; + + } + + // Insert cell weights + int cell_weight_index = 0; + std::string weight_line = ""; + while (cell_weight_index < cell_num) { + weight_line += std::to_string(cell_weight_arr[cell_weight_index]) + " "; + cell_weight_index++; + } + weight_line.erase(weight_line.size() - 1); + patohFile << weight_line; + + } + } + patohFile.close(); + +} + +#ifndef _HEADER_ONLY +#include "init/patoh_writer.inc" +#endif +}// namespace sparsebase::io \ No newline at end of file diff --git a/src/sparsebase/io/patoh_writer.h b/src/sparsebase/io/patoh_writer.h new file mode 100644 index 00000000..2aa397da --- /dev/null +++ b/src/sparsebase/io/patoh_writer.h @@ -0,0 +1,33 @@ +#ifndef SPARSEBASE_PROJECT_PATOH_WRITER_H +#define SPARSEBASE_PROJECT_PATOH_WRITER_H +#include +#include "sparsebase/config.h" +#include "sparsebase/io/writer.h" +#include "sparsebase/object/object.h" + +namespace sparsebase::io { + +template +class PatohWriter: public Writer, + public WritesHyperGraph { + +public: + +explicit PatohWriter(std::string filename, bool is_zero_indexed = false , bool is_edge_weighted = false, + bool is_vertex_weighted = false, int constraint_num = 1); + +void WriteHyperGraph(object::HyperGraph *hyperGraph) const override; +~PatohWriter() override = default; + +private: +std::string filename_; +bool is_zero_indexed_; +bool is_edge_weighted_; +bool is_vertex_weighted_; +}; + +} // namespace sparsebase::io +#ifdef _HEADER_ONLY +#include "patoh_writer.cc" +#endif +#endif // SPARSEBASE_PROJECT_PATOH_WRITER_H \ No newline at end of file diff --git a/src/sparsebase/io/pigo_mtx_reader.cc b/src/sparsebase/io/pigo_mtx_reader.cc index a6eb428c..ac5d6ae4 100644 --- a/src/sparsebase/io/pigo_mtx_reader.cc +++ b/src/sparsebase/io/pigo_mtx_reader.cc @@ -1,8 +1,9 @@ #include "pigo_mtx_reader.h" #include +#include #include - +#include "sparsebase/utils/logger.h" #include "sparsebase/config.h" #include "sparsebase/io/mtx_reader.h" #ifdef USE_PIGO @@ -14,7 +15,19 @@ PigoMTXReader::PigoMTXReader( std::string filename, bool weighted, bool convert_to_zero_index) : filename_(filename), weighted_(weighted), - convert_to_zero_index_(convert_to_zero_index) {} + convert_to_zero_index_(convert_to_zero_index) { + std::ifstream fin(filename_); + + if (fin.is_open()) { + std::string header_line; + std::getline(fin, header_line); + // parse first line + options_ = ParseHeader(header_line); + } else { + throw utils::ReaderException("Wrong matrix market file name\n"); + } + + } // template // format::Array * @@ -23,12 +36,117 @@ PigoMTXReader::PigoMTXReader( // return reader.ReadArray(); //} +template +typename PigoMTXReader::MTXOptions +PigoMTXReader::ParseHeader( + std::string header_line) const { + std::stringstream line_ss(header_line); + MTXOptions options; + std::string prefix, object, format, field, symmetry; + line_ss >> prefix >> object >> format >> field >> symmetry; + if (prefix != MMX_PREFIX) + throw utils::ReaderException("Wrong prefix in a matrix market file"); + // parsing Object option + if (object == "matrix") { + options.object = + PigoMTXReader::MTXObjectOptions::matrix; + } else if (object == "vector") { + options.object = + PigoMTXReader::MTXObjectOptions::matrix; + throw utils::ReaderException( + "Matrix market reader does not currently support reading vectors."); + } else { + throw utils::ReaderException( + "Illegal value for the 'object' option in matrix market header"); + } + // parsing format option + if (format == "array") { + options.format = + PigoMTXReader::MTXFormatOptions::array; + } else if (format == "coordinate") { + options.format = + PigoMTXReader::MTXFormatOptions::coordinate; + } else { + throw utils::ReaderException( + "Illegal value for the 'format' option in matrix market header"); + } + // parsing field option + if (field == "real") { + options.field = + PigoMTXReader::MTXFieldOptions::real; + if constexpr (std::is_same::value) + throw utils::ReaderException( + "You are reading the values of the matrix market file into a void " + "array"); + } else if (field == "double") { + options.field = + PigoMTXReader::MTXFieldOptions::double_field; + if constexpr (std::is_same::value) + throw utils::ReaderException( + "You are reading the values of the matrix market file into a void " + "array"); + } else if (field == "complex") { + options.field = + PigoMTXReader::MTXFieldOptions::complex; + if constexpr (std::is_same::value) + throw utils::ReaderException( + "You are reading the values of the matrix market file into a void " + "array"); + } else if (field == "integer") { + options.field = + PigoMTXReader::MTXFieldOptions::integer; + if constexpr (std::is_same::value) + throw utils::ReaderException( + "You are reading the values of the matrix market file into a void " + "array"); + } else if (field == "pattern") { + options.field = + PigoMTXReader::MTXFieldOptions::pattern; + } else { + throw utils::ReaderException( + "Illegal value for the 'field' option in matrix market header"); + } + // parsing symmetry + if (symmetry == "general") { + options.symmetry = + PigoMTXReader::MTXSymmetryOptions::general; + } else if (symmetry == "symmetric") { + options.symmetry = + PigoMTXReader::MTXSymmetryOptions::symmetric; + } else if (symmetry == "skew-symmetric") { + options.symmetry = PigoMTXReader::MTXSymmetryOptions::skew_symmetric; + } else if (symmetry == "hermitian") { + options.symmetry = + PigoMTXReader::MTXSymmetryOptions::hermitian; + throw utils::ReaderException( + "Matrix market reader does not currently support hermitian symmetry."); + } else { + throw utils::ReaderException( + "Illegal value for the 'symmetry' option in matrix market header"); + } + return options; +} + template format::COO *PigoMTXReader::ReadCOO() const { #ifdef USE_PIGO + format::COO *coo; + if (options_.object != matrix || + options_.format != coordinate || + options_.symmetry != general) + { + utils::Logger logger(typeid(this)); + logger.Log("PIGO is not equipped to read these options, defaulting to sequential reader...", utils::LOG_LVL_WARNING); + + sparsebase::io::MTXReader reader(filename_); + coo = reader.ReadCOO(); + return coo; + } + if (weighted_) { if constexpr (!std::is_same_v) { pigo::COO *arr) const = 0; }; +//! Interface for writers that can write an Graph instance to a file +template +class WritesGraph { + //! Writes the given Graph instance to a file + virtual void WriteGraph(object::Graph *graph) const = 0; +}; + +//! Interface for writers that can write an HyperGraph instance to a file +template +class WritesHyperGraph { + //! Writes the given HyperGraph instance to a file + virtual void WriteHyperGraph(object::HyperGraph *hyperGraph) const = 0; +}; + } // namespace io } // namespace sparsebase diff --git a/src/sparsebase/object/object.cc b/src/sparsebase/object/object.cc index 122c430c..8eebbc32 100644 --- a/src/sparsebase/object/object.cc +++ b/src/sparsebase/object/object.cc @@ -87,6 +87,16 @@ Graph::Graph(format::Format *connectivity) { this->VerifyStructure(); InitializeInfoFromConnection(); } + +template +Graph::Graph(format::Format *connectivity, NumEdges ncon, format::Array** vertexWeights) { + this->set_connectivity(connectivity, true); + this->VerifyStructure(); + InitializeInfoFromConnection(); + this->ncon_ = ncon; + this->vertexWeights_ = vertexWeights; +} + template void Graph::ReadConnectivityToCOO( const io::ReadsCOO &reader) { @@ -147,6 +157,35 @@ void Graph::VerifyStructure() { // check dimensions } +template +HyperGraph::HyperGraph(){} + +template +HyperGraph::HyperGraph(format::Format *connectivity, VertexID base_type, VertexID constraint_num, format::CSR *xNetCSR){ + this->set_connectivity(connectivity, true); + this->VerifyStructure(); + this->InitializeInfoFromConnection(); + this->base_type_ = base_type; + this->constraint_num_ = constraint_num; + this->xNetCSR_ = xNetCSR; +} + +template +HyperGraph::HyperGraph(format::Format *connectivity,format::Array *netWeights,format::Array *cellWeights, VertexID base_type, VertexID constraint_num, format::CSR *xNetCSR){ + this->set_connectivity(connectivity, true); + this->VerifyStructure(); + this->InitializeInfoFromConnection(); + this->base_type_ = base_type; + this->constraint_num_ = constraint_num; + this->xNetCSR_ = xNetCSR; + this->netWeights_ = netWeights; + this->cellWeights_ = cellWeights; +} + +template +HyperGraph::~HyperGraph(){}; + + #if !defined(_HEADER_ONLY) #include "init/object.inc" #endif diff --git a/src/sparsebase/object/object.h b/src/sparsebase/object/object.h index 4fb914e2..e2bdfb8b 100644 --- a/src/sparsebase/object/object.h +++ b/src/sparsebase/object/object.h @@ -16,6 +16,9 @@ #include "sparsebase/format/format.h" #include "sparsebase/format/format_order_one.h" #include "sparsebase/format/format_order_two.h" +#include "sparsebase/format/array.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/format/array.h" #include "sparsebase/io/reader.h" namespace sparsebase { @@ -49,6 +52,7 @@ template class Graph : public AbstractObject { public: Graph(format::Format *connectivity); + Graph(format::Format *connectivity, NumEdges ncon, format::Array** vertexWeights); Graph(); Graph(const Graph &); Graph(Graph &&); @@ -63,8 +67,24 @@ class Graph : public AbstractObject { void VerifyStructure(); VertexID n_; NumEdges m_; + //! Number of vertex weights + NumEdges ncon_ = 0; + format::Array** vertexWeights_ = nullptr; }; +template +class HyperGraph : public Graph { + public: + HyperGraph(); + HyperGraph(format::Format *connectivity,VertexID base_type,VertexID constraint_num,format::CSR *xNetCSR); + HyperGraph(format::Format *connectivity, format::Array *netWeights, format::Array *cellWeights, VertexID base_type, VertexID constraint_num, format::CSR *xNetCSR); + virtual ~HyperGraph(); + VertexID constraint_num_ = 1; + VertexID base_type_; + format::CSR *xNetCSR_; + format::Array *netWeights_ = nullptr; + format::Array *cellWeights_ = nullptr; +}; // template // class TemporalGraph : public AbstractSparseObject{ // public: diff --git a/src/sparsebase/reorder/boba_reorder.cc b/src/sparsebase/reorder/boba_reorder.cc new file mode 100644 index 00000000..71d13f95 --- /dev/null +++ b/src/sparsebase/reorder/boba_reorder.cc @@ -0,0 +1,142 @@ +#include "sparsebase/reorder/boba_reorder.h" + +#include "sparsebase/reorder/reorderer.h" +#include "sparsebase/utils/logger.h" + +#include "sparsebase/format/coo.h" +#include +#include +#include + +namespace sparsebase::reorder { +template +BOBAReorder::BOBAReorder(bool sequential) { + + auto params_struct = new BOBAReorderParams; + params_struct->sequential = sequential; + this->params_ = std::unique_ptr(params_struct); + + this->RegisterFunction( + {format::COO::get_id_static()}, + GetReorderCOO); +} +template +BOBAReorder::BOBAReorder(BOBAReorderParams p) + : BOBAReorder(p.sequential) {} +/*{ + this->RegisterFunction( + {format::COO::get_id_static()}, + GetReorderCOO); +}*/ + +template +IDType *BOBAReorder::GetReorderCOO( + std::vector formats, utils::Parameters *params) { + format::COO *coo = + formats[0]->AsAbsolute>(); + + context::CPUContext *cpu_context = + static_cast(coo->get_context()); + + BOBAReorderParams *params_ = static_cast(params); + + IDType nodes = 0; + IDType nnzs = coo->get_num_nnz(); + + if (coo->get_dimensions()[0] >= coo->get_dimensions()[1]) { + nodes = coo->get_dimensions()[0]; + } else { + nodes = coo->get_dimensions()[1]; + } + + IDType *order = new IDType[nodes](); + IDType *order2 = new IDType[nodes]; + auto coo_col = coo->get_col(); + auto coo_row = coo->get_row(); + + int k = 0; + + std::vector> cooMatrix(nnzs); + for (int i = 0; i < nnzs; i++) { + cooMatrix[i] = std::make_pair(coo_row[i], coo_col[i]); + } + + /* Sort the matrix based on columns */ + std::sort(cooMatrix.begin(), cooMatrix.end(), [](const auto& a, const auto& b) { + return (a.second != b.second) ? (a.second < b.second) : (a.first < b.first); + }); + + if (params_->sequential) { + + /* Goes through the values in the I array (rows) */ + std::unordered_set copiedValues; + for (int i = 0; i < nnzs; i++) { + IDType element_i = cooMatrix[i].first; + if (copiedValues.find(element_i) == copiedValues.end()) { + order[k++] = element_i; + copiedValues.insert(element_i); + } + } + if (k == nodes) { + for (IDType i = 0; i < nodes; i++) { + order2[order[i]] = i; + } + return order2; + } + + /* Goes through the values in the J array (columns) */ + for (int i = 0; i < nnzs; i++) { + IDType element_j = cooMatrix[i].second; + if (copiedValues.find(element_j) == copiedValues.end()) { + order[k++] = element_j; + copiedValues.insert(element_j); + } + } + if (k == nodes) { + for (IDType i = 0; i < nodes; i++) { + order2[order[i]] = i; + } + return order2; + } + + /* Goes through the single nodes that don't have any edge */ + for (IDType i = 0; i < nodes; i++) { + if (copiedValues.find(i) == copiedValues.end()) { + order[k++] = i; + copiedValues.insert(i); + } + order2[order[i]] = i; + } + return order2; + } else { + + std::priority_queue, std::vector>, std::greater>> PQ; + for (IDType i = 0; i < nodes; i++) { + order[i] = nnzs*2; + } + + /* Goes through the values in the I++J array */ + #pragma omp parallel for + for (IDType i = 0; i < nnzs*2; i++) { + if ((i < nnzs) && (i < order[cooMatrix[i].first])) + order[cooMatrix[i].first] = i; + else if ((i >= nnzs) && (i < order[cooMatrix[i - nnzs].second])) + order[cooMatrix[i - nnzs].second] = i; + } + + /* Places vertices by order of appearence */ + for (IDType i = 0; i < nodes; i++) { + PQ.push(std::make_pair(order[i], i)); + } + for (IDType i = 0; i < nodes; i++) { + order2[PQ.top().second] = i; + PQ.pop(); + } + return order2; + } +} + +#if !defined(_HEADER_ONLY) +#include "init/boba_reorder.inc" +#endif +} // namespace sparsebase::reorder diff --git a/src/sparsebase/reorder/boba_reorder.h b/src/sparsebase/reorder/boba_reorder.h new file mode 100644 index 00000000..a0689c3f --- /dev/null +++ b/src/sparsebase/reorder/boba_reorder.h @@ -0,0 +1,48 @@ +#include +#include + +#include "sparsebase/config.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/reorder/reorderer.h" +#include "sparsebase/utils/parameterizable.h" +#ifndef SPARSEBASE_PROJECT_BOBA_REORDER_H +#define SPARSEBASE_PROJECT_BOBA_REORDER_H + +namespace sparsebase::reorder { + +struct BOBAReorderParams : utils::Parameters { + bool sequential; + explicit BOBAReorderParams() {} + BOBAReorderParams(bool sequential_) + : sequential(sequential_) {} +}; + +template +class BOBAReorder : public Reorderer { + typedef typename std::make_signed::type SignedID; + + public: + //! Parameter type for GrayReorder + typedef BOBAReorderParams ParamsType; + BOBAReorder(bool sequential); + BOBAReorder(ParamsType p); + + protected: + //! An implementation function that will reorder a COO format + /*! + * + * @param formats a vector containing a single Format object of type COO + * @param params a polymorphic pointer at a `RCMReorderParams` object + * @return an inverse permutation of the input format; an array of size + * `format.get_dimensions()[0]` where the ith element is the order of the ith + * element in the original format object + */ + static IDType *GetReorderCOO(std::vector formats, + utils::Parameters *); +}; + +} // namespace sparsebase::reorder +#ifdef _HEADER_ONLY +#include "sparsebase/reorder/boba_reorder.cc" +#endif +#endif // SPARSEBASE_PROJECT_BOBA_REORDER_H diff --git a/src/sparsebase/reorder/degree_reorder.cc b/src/sparsebase/reorder/degree_reorder.cc index f5a80eaa..e4a46db2 100644 --- a/src/sparsebase/reorder/degree_reorder.cc +++ b/src/sparsebase/reorder/degree_reorder.cc @@ -26,13 +26,14 @@ IDType *DegreeReorder::CalculateReorderCSR( DegreeReorderParams *cast_params = static_cast(params); bool ascending = cast_params->ascending; IDType n = csr->get_dimensions()[0]; - IDType *counts = new IDType[n](); + IDType *counts = new IDType[n + 1](); auto row_ptr = csr->get_row_ptr(); auto col = csr->get_col(); + for (IDType u = 0; u < n; u++) { - counts[row_ptr[u + 1] - row_ptr[u] + 1]++; + counts[row_ptr[u + 1] - row_ptr[u]]++; } - for (IDType u = 1; u < n; u++) { + for (IDType u = 1; u < n + 1; u++) { counts[u] += counts[u - 1]; } IDType *sorted = new IDType[n]; @@ -40,7 +41,7 @@ IDType *DegreeReorder::CalculateReorderCSR( IDType *mr = new IDType[n](); for (IDType u = 0; u < n; u++) { IDType ec = counts[row_ptr[u + 1] - row_ptr[u]]; - sorted[ec + mr[ec]] = u; + sorted[ec - mr[ec] - 1] = u; mr[ec]++; } if (!ascending) { diff --git a/src/sparsebase/reorder/gray_reorder.cc b/src/sparsebase/reorder/gray_reorder.cc index cbeb2cc1..0391653f 100644 --- a/src/sparsebase/reorder/gray_reorder.cc +++ b/src/sparsebase/reorder/gray_reorder.cc @@ -35,12 +35,13 @@ bool GrayReorder::asc_comparator( template // not sure if all IDTypes work for this -unsigned long GrayReorder::grey_bin_to_dec( - unsigned long n) { - unsigned long inv = 0; - - for (; n; n = n >> 1) inv ^= n; +unsigned long long GrayReorder::grey_bin_to_dec( + unsigned long long n) { + unsigned long long inv = 0; + for (; n; n = n >> 1) { + inv ^= n; + } return inv; } @@ -81,16 +82,18 @@ bool GrayReorder::is_banded( std::vector order, int band_size) { if (band_size == -1) band_size = n_cols / 64; int band_count = 0; + int nnz_ = 0; bool banded = false; for (int r = 0; r < order.size(); r++) { for (int i = row_ptr[order[r]]; i < row_ptr[order[r] + 1]; i++) { int col = cols[i]; - if (abs(col - r) <= band_size) band_count++; + nnz_++; + if ((col >= r) ? (col - r <= band_size) : (r - col <= band_size)) band_count++; } } - - if (double(band_count) / nnz >= 0.3) { + + if (double(band_count) / nnz_ >= 0.3) { banded = true; } utils::Logger logger(typeid(GrayReorder)); @@ -107,6 +110,8 @@ IDType *GrayReorder::GrayReorderingCSR( static_cast(csr->get_context()); IDType n_rows = csr->get_dimensions()[0]; + IDType n_cols = csr->get_dimensions()[1]; + /*This array stores the permutation vector such as order[0] = 243 means that * row 243 is the first row of the reordered matrix*/ IDType *order = new IDType[n_rows](); @@ -116,7 +121,7 @@ IDType *GrayReorder::GrayReorderingCSR( int bit_resolution = params->resolution; int raise_to = 0; - int adder = 0; + unsigned long long adder = 0; int start_split_reorder, end_split_reorder; int last_row_nnz_count = 0; @@ -124,12 +129,22 @@ IDType *GrayReorder::GrayReorderingCSR( bool decresc_grey_order = false; int group_count = 0; + + /*added*/ + int sparse_diagonal = 0; + int dense_diagonal = 0; + int nnz_sparse = 0; + int nnz_dense = 0; + int band_size = csr->get_dimensions()[1] / 128; // Initializing row order std::vector v_order; std::vector sparse_v_order; std::vector dense_v_order; + sparse_v_order.reserve(n_rows); + dense_v_order.reserve(n_rows); + // Splitting original matrix's rows in two submatrices IDType sparse_dense_split = 0; for (IDType i = 0; i < n_rows; i++) { @@ -137,26 +152,46 @@ IDType *GrayReorder::GrayReorderingCSR( params->nnz_threshold) { sparse_v_order.push_back(i); sparse_dense_split++; + /* Counts how many non-zeros are in the sparse sub-matrix diagonal */ + for (int j = csr->get_row_ptr()[i]; j < csr->get_row_ptr()[i + 1]; j++) { + int col = csr->get_col()[j]; + nnz_sparse++; + if ((col >= i) ? (col - i <= band_size) : (i - col <= band_size)) sparse_diagonal++; + } } else { dense_v_order.push_back(i); + /* Counts how many non-zeros are in the dense sub-matrix diagonal */ + for (int j = csr->get_row_ptr()[i]; j < csr->get_row_ptr()[i + 1]; j++) { + int col = csr->get_col()[j]; + nnz_dense++; + if ((col >= i) ? (col - i <= band_size) : (i - col <= band_size)) dense_diagonal++; + } } } - v_order.reserve(sparse_v_order.size() + dense_v_order.size()); // preallocate memory - bool is_sparse_banded = - is_banded(csr->get_num_nnz(), csr->get_dimensions()[1], - csr->get_row_ptr(), csr->get_col(), sparse_v_order); utils::Logger logger(typeid(GrayReorder)); + + /* ADDED */ + /* Checks of sub-matrices are highly banded or not */ + bool is_sparse_banded; + bool is_dense_banded; + + if (double(sparse_diagonal) / nnz_sparse > 0.3) { + is_sparse_banded = true; + } else { + is_sparse_banded = false; + } + if (double(dense_diagonal) / nnz_dense > 0.2) { + is_dense_banded = true; + } else { + is_dense_banded = false; + } + if (is_sparse_banded) - logger.Log( - "Sparse Sub-Matrix highly banded - Performing just density reordering", + logger.Log("Sparse Sub-Matrix highly banded - Performing just density reordering", utils::LogLevel::LOG_LVL_INFO); - - bool is_dense_banded = - is_banded(csr->get_num_nnz(), csr->get_dimensions()[1], - csr->get_row_ptr(), csr->get_col(), dense_v_order); if (is_dense_banded) logger.Log("Dense Sub-Matrix highly banded - Maintaining structure", utils::LogLevel::LOG_LVL_INFO); @@ -168,25 +203,25 @@ IDType *GrayReorder::GrayReorderingCSR( }); // reorder sparse matrix into nnz amount // the bit resolution determines the width of the bitmap of each row - if (n_rows < bit_resolution) { - bit_resolution = n_rows; + if (n_cols < bit_resolution) { + bit_resolution = n_cols; } - int row_split = n_rows / bit_resolution; + int row_split = n_cols / bit_resolution; auto nnz_per_row_split = new IDType[bit_resolution]; auto nnz_per_row_split_bin = new IDType[bit_resolution]; - unsigned long decimal_bit_map = 0; - unsigned long dec_begin = 0; + unsigned long long decimal_bit_map = 0; + unsigned long long dec_begin = 0; int dec_begin_ind = 0; std::vector reorder_section; // vector that contains a section to be reordered + reorder_section.reserve(n_rows); if (!is_sparse_banded) { // if banded just row ordering by nnz count is // enough, else do bitmap reordering in groups - for (int i = 0; i < sparse_v_order.size(); i++) { // sparse sub matrix if not highly banded if (i == 0) { @@ -218,6 +253,7 @@ IDType *GrayReorder::GrayReorderingCSR( // get bitmap of the row in decimal value (first rows are less significant // bits) + decimal_bit_map = 0; for (int j = 0; j < bit_resolution; j++) { adder = 0; @@ -291,7 +327,7 @@ IDType *GrayReorder::GrayReorderingCSR( group_count = 0; } } - + // if(decimal_bit_map != 0){ // for(int i = 0; i < bit_resolution; i++){ // std::cout << "[" << nnz_per_row_split_bin[(bit_resolution-1)-i] << @@ -303,10 +339,11 @@ IDType *GrayReorder::GrayReorderingCSR( // } // - + reorder_section.push_back( row_grey_pair(sparse_v_order[i], grey_bin_to_dec(decimal_bit_map))); + // when reaching end of sparse submatrix, reorder section if (i == sparse_v_order.size() - 1) { end_split_reorder = sparse_v_order.size(); @@ -326,10 +363,9 @@ IDType *GrayReorder::GrayReorderingCSR( } } } - + reorder_section.clear(); } - if (!is_dense_banded) { logger.Log("Rows [" + std::to_string(sparse_dense_split) + "-" + std::to_string(n_rows) + @@ -339,6 +375,7 @@ IDType *GrayReorder::GrayReorderingCSR( for (int i = 0; i < dense_v_order.size(); i++) { // if first row, establish the nnz amount, and starting index for (int j = 0; j < bit_resolution; j++) nnz_per_row_split[j] = 0; + for (int j = 0; j < bit_resolution; j++) nnz_per_row_split_bin[j] = 0; for (int k = csr->get_row_ptr()[dense_v_order[i]]; k < csr->get_row_ptr()[dense_v_order[i] + 1]; k++) { @@ -351,12 +388,14 @@ IDType *GrayReorder::GrayReorderingCSR( for (int j = 0; j < bit_resolution; j++) { adder = 0; if (nnz_per_row_split[j] > threshold) { + nnz_per_row_split_bin[j] = 1; raise_to = j; // row 0 = lowest significant bit adder = pow(2, raise_to); decimal_bit_map = decimal_bit_map + adder; } } + reorder_section.push_back( row_grey_pair(dense_v_order[i], grey_bin_to_dec(decimal_bit_map))); } @@ -370,7 +409,6 @@ IDType *GrayReorder::GrayReorderingCSR( reorder_section.clear(); } - v_order.insert(v_order.end(), sparse_v_order.begin(), sparse_v_order.end()); v_order.insert(v_order.end(), dense_v_order.begin(), dense_v_order.end()); @@ -380,8 +418,8 @@ IDType *GrayReorder::GrayReorderingCSR( for (int i = 0; i < n_rows; i++) { order[v_order[i]] = i; } - // std::copy(v_order_inv.begin(), v_order_inv.end(), order); + // std::copy(v_order_inv.begin(), v_order_inv.end(), order); return order; } diff --git a/src/sparsebase/reorder/gray_reorder.h b/src/sparsebase/reorder/gray_reorder.h index 9f2e4ba7..ecda4117 100644 --- a/src/sparsebase/reorder/gray_reorder.h +++ b/src/sparsebase/reorder/gray_reorder.h @@ -12,8 +12,8 @@ namespace sparsebase::reorder { enum BitMapSize{ BitSize16 = 16, - BitSize32 = 32/*, - BitSize64 = 64*/ //at the moment, using 64 bits is not working as intended + BitSize32 = 32, + BitSize64 = 64 }; //! Params struct for GrayReorder struct GrayReorderParams : utils::Parameters { @@ -44,7 +44,7 @@ class GrayReorder : public Reorderer { static bool asc_comparator(const row_grey_pair &l, const row_grey_pair &r); // not sure if all IDTypes work for this - static unsigned long grey_bin_to_dec(unsigned long n); + static unsigned long long grey_bin_to_dec(unsigned long long n); static void print_dec_in_bin(unsigned long n, int size); diff --git a/src/sparsebase/reorder/rcm_reorder.cc b/src/sparsebase/reorder/rcm_reorder.cc index 450ccc51..ac4ad1c3 100644 --- a/src/sparsebase/reorder/rcm_reorder.cc +++ b/src/sparsebase/reorder/rcm_reorder.cc @@ -27,16 +27,18 @@ IDType RCMReorder::peripheral(NNZType *xadj, IDType r = start; SignedID rlevel = -1; SignedID qlevel = 0; + SignedID deg = -1, flag = -1; + /* Repeat with the new found root. */ + /* If the distance is the same then return the current root */ while (rlevel != qlevel) { - // cout << "Finding peripheral: current dist = " << qlevel << std::endl;; rlevel = qlevel; - for (IDType i = 0; i < n; i++) distance[i] = -1; IDType qrp = 0, qwp = 0; distance[r] = 0; Q[qwp++] = r; + /* Computes the distance to the root of every node in the connected component */ while (qrp < qwp) { IDType u = Q[qrp++]; for (NNZType ptr = xadj[u]; ptr < xadj[u + 1]; ptr++) { @@ -44,15 +46,34 @@ IDType RCMReorder::peripheral(NNZType *xadj, if (distance[v] == (IDType)-1) { distance[v] = distance[u] + 1; Q[qwp++] = v; + if (distance[v] > qlevel) { + qlevel = distance[v]; + } } } } - qlevel = 0; - for (IDType i = 0; i < qrp; i++) { - if (qlevel < distance[Q[i]]) { - qlevel = distance[Q[i]]; - r = Q[i]; + /* If the number of levels is the same as the number of nodes in the */ + /* connected component (N-N-N-N-N) then we already have the root */ + if (qrp == qlevel + 1) {return r;} + + /* Goes through nodes in the connected component. */ + /* Root will be the node with maximum distance and lowest degree */ + flag = -1; + if (rlevel != qlevel) { + for (IDType i = 0; i < qrp; i++) { + if (qlevel == distance[Q[i]]) { + if (flag == -1) { + deg = xadj[Q[i] + 1] - xadj[Q[i]] + 1; + flag = 0; + } + if (xadj[Q[i] + 1] - xadj[Q[i]] < deg) { + qlevel = distance[Q[i]]; + r = Q[i]; + deg = xadj[Q[i] + 1] - xadj[Q[i]]; + } + } + distance[Q[i]] = -1; } } } @@ -67,30 +88,45 @@ IDType *RCMReorder::GetReorderCSR( IDType *adj = csr->get_col(); IDType n = csr->get_dimensions()[0]; IDType *Q = new IDType[n]; - + IDType *Qp = new IDType[n]; + IDType *Qp2 = new IDType[n]; SignedID *distance = new SignedID[n]; IDType *V = new IDType[n]; - for (IDType i = 0; i < n; i++) V[i] = 0; - std::priority_queue> PQ; - int qrp = 0, qwp = 0; + for (IDType i = 0; i < n; i++) { + distance[i] = -1; + V[i] = 0; + } + std::priority_queue, std::vector>, std::greater>> PQ; + int qrp = 0, qwp = 0, qst = 0; IDType reverse = n - 1; + /* Go through every node in the graph */ for (IDType i = 0; i < n; i++) { + + /* If the node has not been visited yet then it belongs to a new connected component */ if (V[i] == 0) { + + /* Connected components that consist of a single node don't need reordering */ if (xadj[i] == xadj[i + 1]) { - Q[reverse--] = i; + Q[qwp] = i; + Qp2[qwp++] = i; V[i] = 1; continue; } - // cout << i << std::endl; + /* Find approximate peripheral node */ IDType perv = peripheral(xadj, adj, n, i, distance, Qp); + qst = qwp; V[perv] = 1; Q[qwp++] = perv; + /* BFS search of the connected component */ while (qrp < qwp) { IDType u = Q[qrp++]; + + /* Visit all unvisited nodes neighbouring the current node. */ + /* Order based on their degree */ for (IDType ptr = xadj[u]; ptr < xadj[u + 1]; ptr++) { IDType v = adj[ptr]; if (V[v] == 0) { @@ -99,23 +135,28 @@ IDType *RCMReorder::GetReorderCSR( } } + /* Place the neighbouring nodes in the queue with their new degree order */ while (!PQ.empty()) { Q[qwp++] = PQ.top().second; ; PQ.pop(); } } - } - } - // Reverse - for (IDType i = 0; i < n / 2; i++) { - Qp[i] = Q[n - i - 1]; - Qp[n - i - 1] = Q[i]; + /* Reverse connected component */ + for (IDType j = qst; j < qst + (qwp-qst)/2; j++) { + Qp2[j] = Q[qwp-1 - (j - qst)]; + Qp2[qwp-1 - (j - qst)] = Q[j]; + } + if ((qwp-qst)%2 != 0) { + Qp2[qst + (qwp-1 - qst)/2] = Q[qst + (qwp-1 - qst)/2]; + } + } } - // Place it in the form that the transform function takes + + /* Place it in the form that the transform function takes */ for (IDType i = 0; i < n; i++) { - Q[Qp[i]] = i; + Q[Qp2[i]] = i; } delete[] Qp; @@ -127,4 +168,4 @@ IDType *RCMReorder::GetReorderCSR( #if !defined(_HEADER_ONLY) #include "init/rcm_reorder.inc" #endif -} // namespace sparsebase::reorder +} // namespace sparsebase::reorder \ No newline at end of file diff --git a/src/sparsebase/reorder/slashburn_reorder.cc b/src/sparsebase/reorder/slashburn_reorder.cc new file mode 100644 index 00000000..4b4391cc --- /dev/null +++ b/src/sparsebase/reorder/slashburn_reorder.cc @@ -0,0 +1,424 @@ +#include "sparsebase/reorder/slashburn_reorder.h" + +#include "sparsebase/reorder/reorderer.h" +#include "sparsebase/utils/logger.h" + +#include "sparsebase/format/csr.h" + +#include +#include + +bool GREEDY; +bool HUB_ORDER; + +/*SQ*/ +/* 1ºst imp - greedy k-hub selection, update degree of nodes */ +/* after removing one of the nodes. */ +/* 2ºnd imp - order spokes by hube order and not by size */ + +namespace sparsebase::reorder { +template +SlashburnReorder::SlashburnReorder(int k_size, bool greedy, bool hub_order) { + auto params_struct = new SlashburnReorderParams; + params_struct->k_size = k_size; + params_struct->greedy = greedy; + params_struct->hub_order = hub_order; + this->params_ = std::unique_ptr(params_struct); + + this->RegisterFunction( + {format::CSR::get_id_static()}, + GetReorderCSR); +} +template +SlashburnReorder::SlashburnReorder(SlashburnReorderParams p) + : SlashburnReorder(p.k_size, p.greedy, p.hub_order) {} + /*{ + this->RegisterFunction( + {format::CSR::get_id_static()}, + GetReorderCSR); +}*/ + +template +IDType *SlashburnReorder::computeDegree(NNZType *rptr, + IDType *col, + IDType n, + IDType *v_flag, + IDType level) { + + IDType *degree = new IDType[n]; + + /* Compute the degree of all nodes in the GCC */ + for (IDType i = 0; i < n; i++) { + degree[i] = 0; + if (v_flag[i] == level) { + for (IDType ptr = rptr[i]; ptr < rptr[i + 1]; ptr++) { + IDType node_id = col[ptr]; + if (v_flag[node_id] == level) degree[i]++; + } + } else { + degree[i] = -1; + } + } + return degree; +} + +template +IDType *SlashburnReorder::removeKHubsetGreedy(NNZType *rptr, + IDType *col, + IDType n, IDType k, + IDType *v_flag, + IDType *order, + IDType *degree, + IDType level, + IDType min_id) { + std::priority_queue, std::vector>, std::greater>> PQ; + IDType *k_hub = new IDType[k]; + + /* Removes the highest degree node k times*/ + for (IDType i = 0; i < k; i++) { + /* Finds node of highest degree */ + PQ.push(std::make_pair(degree[0], 0)); + for (IDType i = 0; i < n; i++) { + if (v_flag[i] == level) { + if (degree[i] > PQ.top().first) { + PQ.pop(); + PQ.push(std::make_pair(degree[i], i)); + } + } + } + + /* Remove highest degree node from the graph (flag = 0) */ + IDType u = PQ.top().second; + order[min_id + i] = u; + v_flag[u] = 0; + k_hub[i] = u; + degree[u] = -1; + PQ.pop(); + + /* Update degree of nodes */ + for (IDType ptr = rptr[u]; ptr < rptr[u + 1]; ptr++) { + IDType node_id = col[ptr]; + if (v_flag[node_id] == level) degree[node_id]--; + } + } + + return k_hub; +} + +template +IDType *SlashburnReorder::removeKHubset(NNZType *rptr, + IDType *col, + IDType n, IDType k, + IDType *v_flag, + IDType *order, + IDType level, + IDType min_id) { + std::priority_queue, std::vector>, std::greater>> PQ; + IDType *k_hub = new IDType[k]; + IDType i = 0, j = 0, qwp1 = 0; + + /* Place first k nodes in the stack */ + for (i = 0; i < n; i++) { + if (v_flag[i] == level) { + IDType degree = 0; + for (IDType ptr = rptr[i]; ptr < rptr[i + 1]; ptr++) { + IDType node_id = col[ptr]; + if (v_flag[node_id] == level) degree++; + } + PQ.push(std::make_pair(degree, i)); + j++; + } + if (j == k) break; + } + /* Check rest of the nodes to find high degree ones */ + for (i = i+1; i < n; i++) { + if (v_flag[i] == level) { + IDType degree = 0; + for (IDType ptr = rptr[i]; ptr < rptr[i + 1]; ptr++) { + IDType node_id = col[ptr]; + if (v_flag[node_id] == level) degree++; + } + if (degree > PQ.top().first) { + PQ.pop(); + PQ.push(std::make_pair(degree, i)); + } + } + } + /* Remove them from the graph (flag = 0) */ + while (!PQ.empty()) { + IDType node_id = PQ.top().second; + order[min_id + k - 1 - qwp1] = node_id; + v_flag[node_id] = 0; + k_hub[k - 1 - qwp1] = node_id; + qwp1++; + ; + PQ.pop(); + } + return k_hub; +} + + + +template +IDType SlashburnReorder::findCC(NNZType *rptr, + IDType *col, + IDType *v_flag, + IDType level, + IDType root) { + IDType cc_count = 1; + std::stack DFS; + + DFS.push(root); + v_flag[root] = level + 1; + + /* Goes through all the nodes in the connected component */ + while (!DFS.empty()) { + IDType u = DFS.top(); + DFS.pop(); + + for (IDType ptr = rptr[u]; ptr < rptr[u + 1]; ptr++) { + IDType node_id = col[ptr]; + if (v_flag[node_id] == level) { + DFS.push(node_id); + v_flag[node_id] = level + 1; + cc_count++; + } + } + } + return cc_count; +} + +template +IDType SlashburnReorder::orderCC(NNZType *rptr, + IDType *col, + IDType *v_flag, + IDType *order, + IDType level, + IDType root, + IDType max_id) { + IDType qwp2 = 0; + std::queue DFS; + + DFS.push(root); + order[max_id - qwp2] = root; + v_flag[root] = -level; + qwp2++; + + /* Goes through all the nodes in the connected component and orders them */ + while (!DFS.empty()) { + IDType u = DFS.front(); + DFS.pop(); + for (IDType ptr = rptr[u]; ptr < rptr[u + 1]; ptr++) { + IDType node_id = col[ptr]; + if (v_flag[node_id] == level + 1) { + v_flag[node_id] = -level; + DFS.push(node_id); + order[max_id - qwp2] = node_id; + qwp2++; + } + } + } + return qwp2; +} + +template +void SlashburnReorder::slashloop(NNZType *rptr, + IDType *col, + IDType n, IDType k, + IDType *v_flag, + IDType *order, + IDType level, + IDType max_id) { + + std::priority_queue, std::vector>, std::greater>> PQ_CC_hub; + IDType cmp_counter = 0; + IDType *k_hub = NULL; + + while (true) { + + /* Removes k-hubset */ + if (GREEDY) { + IDType *degree = computeDegree(rptr, col, n, v_flag, level); + k_hub = removeKHubsetGreedy(rptr, col, n, k, v_flag, order, degree, level, (level-2)*k); + } else { + k_hub = removeKHubset(rptr, col, n, k, v_flag, order, level, (level-2)*k); + } + IDType gcc_count = 0, gcc_id = -1; + cmp_counter = 0; + + /* Finds strong connected components */ + for (IDType i = k - 1; i >= 0; i--) { + IDType u = k_hub[i]; + for (IDType ptr = rptr[u]; ptr < rptr[u + 1]; ptr++) { + IDType node_id = col[ptr]; + if (v_flag[node_id] == level) { + IDType n_cc = findCC(rptr, col, v_flag, level, node_id); + if (n_cc > gcc_count) { + gcc_count = n_cc; + gcc_id = node_id; + } + if (HUB_ORDER) { + PQ_CC_hub.push(std::make_tuple(i, n_cc, node_id)); + } else { + PQ_CC_hub.push(std::make_tuple(0, n_cc, node_id)); + } + cmp_counter++; + } + } + } + /* When there are no more nodes left to reorder */ + if (cmp_counter == 0) + break; + + /* Places spokes in the permutation vector */ + for (IDType i = 0; i < cmp_counter; i++) { + std::tuple root = PQ_CC_hub.top(); + if (std::get<2>(root) == gcc_id) { + PQ_CC_hub.pop(); + continue; + } + PQ_CC_hub.pop(); + IDType n_cc = orderCC(rptr, col, v_flag, order, level, std::get<2>(root), n-1-max_id); + max_id += n_cc; + } + + /* Checks size of GCC */ + if (gcc_count < k) { + IDType n_cc = orderCC(rptr, col, v_flag, order, level, gcc_id, n-1-max_id); + break; + } else { + level++; + } + } + return; +} + +template +IDType *SlashburnReorder::GetReorderCSR( + std::vector formats, utils::Parameters *poly_params) { + format::CSR *csr = + formats[0]->AsAbsolute>(); + + context::CPUContext *cpu_context = + static_cast(csr->get_context()); + + std::priority_queue, std::vector>, std::greater>> PQ; + SlashburnReorderParams *params = static_cast(poly_params); + int k = params->k_size; + + if (params->greedy) + GREEDY = true; + if (params->hub_order) + HUB_ORDER = true; + + NNZType *rptr = csr->get_row_ptr(); + IDType *col = csr->get_col(); + IDType nodes = csr->get_dimensions()[0]; + + NNZType *t_row = new NNZType[nodes + 1]; + IDType *t_count = new IDType[nodes]; + IDType *t_col = new IDType[csr->get_num_nnz()]; + + IDType *order = new IDType[nodes]; + IDType *order2 = new IDType[nodes]; + IDType *v_flag = new IDType[nodes]; + + IDType cmp_counter = 0, max_id = 0; + + for (IDType i = 0; i < nodes; i++) { + v_flag[i] = 1; + t_count[i] = 0; + } + + /* Create CSC format matrix of the input CSR */ + for (IDType i = 0; i < csr->get_num_nnz(); i++) { + IDType col_id = col[i]; + t_count[col_id]++; + } + + t_row[0] = 0; + for (IDType i = 1; i < nodes; i++) { + t_row[i] = t_row[i-1] + t_count[i-1]; + } + t_row[nodes] = csr->get_num_nnz(); + + for (IDType i = 0; i < nodes; i++) { + for (IDType ptr = rptr[i]; ptr < rptr[i + 1]; ptr++) { + IDType node_id = col[ptr]; + t_col[t_row[node_id] + t_count[node_id] - 1] = i; + t_count[node_id]--; + } + } + + /* Create the symmetric version of the input matrix */ + NNZType *last_row = new NNZType[nodes + 1]; + IDType *last_col = new IDType[csr->get_num_nnz() * 2]; + IDType *s_flag = new IDType[nodes](); + IDType last_c = 0; + + for (IDType i = 0; i < nodes; i++) { + for (IDType ptr = rptr[i]; ptr < rptr[i + 1]; ptr++) { + IDType node_id = col[ptr]; + last_col[last_c] = node_id; + s_flag[node_id] = i+1; + last_c++; + } + for (IDType ptr = t_row[i]; ptr < t_row[i + 1]; ptr++) { + IDType node_id = t_col[ptr]; + if (s_flag[node_id] != i+1) { + last_col[last_c] = node_id; + last_c++; + } + } + last_row[i + 1] = last_c; + } + + last_row[0] = 0; + + /* Free the auxiliary CSC format matrix */ + delete[] s_flag; + delete[] t_row; + delete[] t_count; + delete[] t_col; + + /* Orders the spokes in the original graph */ + for (IDType i = 0; i < nodes; i++) { + if (v_flag[i] == 1) { + IDType n_cc = findCC(last_row, last_col, v_flag, 1, i); + PQ.push(std::make_pair(n_cc, i)); + cmp_counter++; + } + } + for (IDType i = 0; i < cmp_counter - 1; i++) { + IDType root = PQ.top().second; + PQ.pop(); + IDType n_cc = orderCC(last_row, last_col, v_flag, order, 1, root, nodes-1-max_id); + max_id += n_cc; + } + + if (PQ.top().first < k) { + IDType root = PQ.top().second; + PQ.pop(); + IDType n_cc = orderCC(last_row, last_col, v_flag, order, 1, root, nodes-1-max_id); + } else { + PQ.pop(); + /* Call the slashburn loop algorithm to order the GCC */ + slashloop(last_row, last_col, nodes, k, v_flag, order, 2, max_id); + } + + /* Convert to the sparsebase permutation format */ + for (IDType i = 0; i < nodes; i++) { + order2[order[i]] = i; + } + + /* Free rest of allocated memory */ + delete[] order; + delete[] v_flag; + + return order2; +} + +#if !defined(_HEADER_ONLY) +#include "init/slashburn_reorder.inc" +#endif +} // namespace sparsebase::reorder diff --git a/src/sparsebase/reorder/slashburn_reorder.h b/src/sparsebase/reorder/slashburn_reorder.h new file mode 100644 index 00000000..c7801857 --- /dev/null +++ b/src/sparsebase/reorder/slashburn_reorder.h @@ -0,0 +1,63 @@ +#include +#include +#include + +#include "sparsebase/config.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/reorder/reorderer.h" +#include "sparsebase/utils/parameterizable.h" +#ifndef SPARSEBASE_PROJECT_SLASHBURN_REORDER_H +#define SPARSEBASE_PROJECT_SLASHBURN_REORDER_H + +namespace sparsebase::reorder { + +struct SlashburnReorderParams : utils::Parameters { + int k_size; + bool greedy; + bool hub_order; + explicit SlashburnReorderParams() {} + SlashburnReorderParams(int hubset_k_size, bool greedy_alg, bool hub_ordering) + : k_size(hubset_k_size), + greedy(greedy_alg), + hub_order(hub_ordering) {} +}; + +template +class SlashburnReorder : public Reorderer { + typedef typename std::make_signed::type SignedID; + + public: + //! Parameter type for GrayReorder + typedef SlashburnReorderParams ParamsType; + SlashburnReorder(int k_size, bool greedy, bool hub_order); + SlashburnReorder(ParamsType p); + + protected: + static void slashloop(NNZType *rptr, IDType *col, IDType n, IDType k, + IDType *v_flag, IDType *order, IDType level, IDType max_id); + + static IDType* removeKHubset(NNZType *rptr, IDType *col, IDType n, IDType k, IDType *v_flag, IDType *order, IDType level, IDType min_id); + static IDType* removeKHubsetGreedy(NNZType *rptr, IDType *col, IDType n, IDType k, IDType *v_flag, IDType *order, IDType *degree, IDType level, IDType min_id); + static IDType* computeDegree(NNZType *rptr, IDType *col, IDType n, IDType *v_flag, IDType level); + + static IDType findCC(NNZType *rptr, IDType *col, IDType *v_flag, IDType level, IDType root); + static IDType orderCC(NNZType *rptr, IDType *col, IDType *v_flag, IDType *order, IDType level, IDType root, IDType max_id); + + //! An implementation function that will reorder a COO format + /*! + * + * @param formats a vector containing a single Format object of type COO + * @param params a polymorphic pointer at a `RCMReorderParams` object + * @return an inverse permutation of the input format; an array of size + * `format.get_dimensions()[0]` where the ith element is the order of the ith + * element in the original format object + */ + static IDType *GetReorderCSR(std::vector formats, + utils::Parameters *); +}; + +} // namespace sparsebase::reorder +#ifdef _HEADER_ONLY +#include "sparsebase/reorder/slashburn_reorder.cc" +#endif +#endif // SPARSEBASE_PROJECT_SLASHBURN_REORDER_H diff --git a/src/sparsebase/sparsebase.h b/src/sparsebase/sparsebase.h index 90b71d12..88aba928 100644 --- a/src/sparsebase/sparsebase.h +++ b/src/sparsebase/sparsebase.h @@ -35,6 +35,7 @@ #include "sparsebase/io/binary_writer_order_two.h" #include "sparsebase/io/edge_list_reader.h" #include "sparsebase/io/mtx_reader.h" +#include "sparsebase/io/mtx_writer.h" #include "sparsebase/io/pigo_edge_list_reader.h" #include "sparsebase/io/pigo_mtx_reader.h" #include "sparsebase/io/reader.h" diff --git a/tests/suites/sparsebase/feature/CMakeLists.txt b/tests/suites/sparsebase/feature/CMakeLists.txt index 9a4bea6b..341d6531 100644 --- a/tests/suites/sparsebase/feature/CMakeLists.txt +++ b/tests/suites/sparsebase/feature/CMakeLists.txt @@ -86,4 +86,104 @@ add_executable(sparsebase_feature_min_max_avg_degree_tests.test min_max_avg_degr target_link_libraries(sparsebase_feature_min_max_avg_degree_tests.test sparsebase) target_link_libraries(sparsebase_feature_min_max_avg_degree_tests.test gtest gtest_main) -add_test(NAME sparsebase_feature_min_max_avg_degree_tests.test COMMAND sparsebase_feature_min_max_avg_degree_tests.test) \ No newline at end of file +add_test(NAME sparsebase_feature_min_max_avg_degree_tests.test COMMAND sparsebase_feature_min_max_avg_degree_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(off_diag_block_nnz_tests.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_feature_off_diag_block_nnz_tests.test off_diag_block_nnz_tests.cc) +target_link_libraries(sparsebase_feature_off_diag_block_nnz_tests.test sparsebase) +target_link_libraries(sparsebase_feature_off_diag_block_nnz_tests.test gtest gtest_main) + +add_test(NAME sparsebase_feature_off_diag_block_nnz_tests.test COMMAND sparsebase_feature_off_diag_block_nnz_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(profile_tests.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_feature_profile_tests.test profile_tests.cc) +target_link_libraries(sparsebase_feature_profile_tests.test sparsebase) +target_link_libraries(sparsebase_feature_profile_tests.test gtest gtest_main) + +add_test(NAME sparsebase_feature_profile_tests.test COMMAND sparsebase_feature_profile_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(bandwidth_tests.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_feature_bandwidth_tests.test bandwidth_tests.cc) +target_link_libraries(sparsebase_feature_bandwidth_tests.test sparsebase) +target_link_libraries(sparsebase_feature_bandwidth_tests.test gtest gtest_main) + +add_test(NAME sparsebase_feature_bandwidth_tests.test COMMAND sparsebase_feature_bandwidth_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(triangle_count_tests.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_feature_triangle_count_tests.test triangle_count_tests.cc) +target_link_libraries(sparsebase_feature_triangle_count_tests.test sparsebase) +target_link_libraries(sparsebase_feature_triangle_count_tests.test gtest gtest_main) + +add_test(NAME sparsebase_feature_triangle_count_tests.test COMMAND sparsebase_feature_triangle_count_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(max_degree_column_tests.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_feature_max_degree_column_tests.test max_degree_column_tests.cc) +target_link_libraries(sparsebase_feature_max_degree_column_tests.test sparsebase) +target_link_libraries(sparsebase_feature_max_degree_column_tests.test gtest gtest_main) + +add_test(NAME sparsebase_feature_max_degree_column_tests.test COMMAND sparsebase_feature_max_degree_column_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(median_degree_column_tests.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_feature_median_degree_column_tests.test median_degree_column_tests.cc) +target_link_libraries(sparsebase_feature_median_degree_column_tests.test sparsebase) +target_link_libraries(sparsebase_feature_median_degree_column_tests.test gtest gtest_main) + +add_test(NAME sparsebase_feature_median_degree_column_tests.test COMMAND sparsebase_feature_median_degree_column_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(avg_degree_column_tests.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_feature_avg_degree_column_tests.test avg_degree_column_tests.cc) +target_link_libraries(sparsebase_feature_avg_degree_column_tests.test sparsebase) +target_link_libraries(sparsebase_feature_avg_degree_column_tests.test gtest gtest_main) + +add_test(NAME sparsebase_feature_avg_degree_column_tests.test COMMAND sparsebase_feature_avg_degree_column_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(min_degree_column_tests.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_feature_min_degree_column_tests.test min_degree_column_tests.cc) +target_link_libraries(sparsebase_feature_min_degree_column_tests.test sparsebase) +target_link_libraries(sparsebase_feature_min_degree_column_tests.test gtest gtest_main) + +add_test(NAME sparsebase_feature_min_degree_column_tests.test COMMAND sparsebase_feature_min_degree_column_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(standard_deviation_degree_column_tests.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_feature_standard_deviation_degree_column_tests.test standard_deviation_degree_column_tests.cc) +target_link_libraries(sparsebase_feature_standard_deviation_degree_column_tests.test sparsebase) +target_link_libraries(sparsebase_feature_standard_deviation_degree_column_tests.test gtest gtest_main) + +add_test(NAME sparsebase_feature_standard_deviation_degree_column_tests.test COMMAND sparsebase_feature_standard_deviation_degree_column_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(coefficient_of_variation_degree_column.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_feature_coefficient_of_variation_degree_column_tests.test coefficient_of_variation_degree_column_tests.cc) +target_link_libraries(sparsebase_feature_coefficient_of_variation_degree_column_tests.test sparsebase) +target_link_libraries(sparsebase_feature_coefficient_of_variation_degree_column_tests.test gtest gtest_main) + +add_test(NAME sparsebase_feature_coefficient_of_variation_degree_column_tests.test COMMAND sparsebase_feature_coefficient_of_variation_degree_column_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(geometric_avg_degree_column_tests.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_feature_geometric_avg_degree_column_tests.test geometric_avg_degree_column_tests.cc) +target_link_libraries(sparsebase_feature_geometric_avg_degree_column_tests.test sparsebase) +target_link_libraries(sparsebase_feature_geometric_avg_degree_column_tests.test gtest gtest_main) + +add_test(NAME sparsebase_feature_geometric_avg_degree_column_tests.test COMMAND sparsebase_feature_geometric_avg_degree_column_tests.test) + diff --git a/tests/suites/sparsebase/feature/avg_degree_column_tests.cc b/tests/suites/sparsebase/feature/avg_degree_column_tests.cc new file mode 100644 index 00000000..3129e9af --- /dev/null +++ b/tests/suites/sparsebase/feature/avg_degree_column_tests.cc @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "sparsebase/bases/reorder_base.h" +#include "sparsebase/context/context.h" +#include "sparsebase/feature/avg_degree_column.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/format/format_order_one.h" +#include "sparsebase/format/format_order_two.h" +#include "sparsebase/reorder/degree_reorder.h" +#include "sparsebase/reorder/reorderer.h" +#include "sparsebase/utils/exception.h" + +using namespace sparsebase; +using namespace sparsebase::reorder; +using namespace sparsebase::bases; +using namespace sparsebase::feature; +#include "../functionality_common.inc" + +class AvgDegreeColumnTest : public ::testing::Test { + protected: + AvgDegreeColumn feature; + + struct Params1 : sparsebase::utils::Parameters {}; + struct Params2 : sparsebase::utils::Parameters {}; +}; + +TEST_F(AvgDegreeColumnTest, AllTests) { + // test get_sub_ids + EXPECT_EQ(feature.get_sub_ids().size(), 1); + EXPECT_EQ(feature.get_sub_ids()[0], std::type_index(typeid(feature))); + + // Test get_subs + auto subs = feature.get_subs(); + // a single sub-feature + EXPECT_EQ(subs.size(), 1); + // same type as feature but different address + auto &feat = *(subs[0]); + EXPECT_EQ(std::type_index(typeid(feat)), std::type_index(typeid(feature))); + EXPECT_NE(subs[0], &feature); + + // Check GetAvgDegreeColumnCSC implementation function + Params1 p1; + auto avg_degree_column = + AvgDegreeColumn::GetAvgDegreeColumnCSC( + {&global_csc}, &p1); + + int degree_sum = col_ptr[n] - col_ptr[0]; + auto avg_in_degrees = degree_sum / (float) n; + + EXPECT_EQ(*avg_degree_column, avg_in_degrees); + + delete avg_degree_column; + //// Check GetAvgDegree (function matcher) + avg_degree_column = + feature.GetAvgDegreeColumn(&global_csc, {&cpu_context}, true); + EXPECT_EQ(*avg_degree_column, avg_in_degrees); + delete avg_degree_column; + + avg_degree_column = + feature.GetAvgDegreeColumn(&global_csc, {&cpu_context}, false); + EXPECT_EQ(*avg_degree_column, avg_in_degrees); + delete avg_degree_column; + + // Check GetAvgDegree with conversion + avg_degree_column = + feature.GetAvgDegreeColumn(&global_coo, {&cpu_context}, true); + EXPECT_EQ(*avg_degree_column, avg_in_degrees); + delete avg_degree_column; + EXPECT_THROW(feature.GetAvgDegreeColumn(&global_coo, {&cpu_context}, false), + utils::DirectExecutionNotAvailableException< + std::vector>); + // Check GetAvgDegree with conversion and cached + auto avg_degree_column_format = + feature.GetAvgDegreeColumnCached(&global_coo, {&cpu_context}, true); + EXPECT_EQ(*std::get<1>(avg_degree_column_format), avg_in_degrees); + delete std::get<1>(avg_degree_column_format); + + auto cached_data = std::get<0>(avg_degree_column_format); + ASSERT_EQ(cached_data.size(), 1); + ASSERT_EQ(cached_data[0][0]->get_id(), std::type_index(typeid(global_csc))); + auto converted_csc = + cached_data[0][0]->AsAbsolute>(); + compare_csc(&global_csc, converted_csc); + // Check Extract + auto feature_map = feature.Extract(&global_csc, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + EXPECT_EQ(*std::any_cast(feature_map[feature.get_id()]), + avg_in_degrees); + + // Check Extract with conversion + feature_map = feature.Extract(&global_coo, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + EXPECT_EQ(*std::any_cast(feature_map[feature.get_id()]), + avg_in_degrees); +} diff --git a/tests/suites/sparsebase/feature/bandwidth_tests.cc b/tests/suites/sparsebase/feature/bandwidth_tests.cc new file mode 100644 index 00000000..cf16946d --- /dev/null +++ b/tests/suites/sparsebase/feature/bandwidth_tests.cc @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "sparsebase/bases/reorder_base.h" +#include "sparsebase/context/context.h" +#include "sparsebase/feature/bandwidth.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/format/format_order_one.h" +#include "sparsebase/format/format_order_two.h" +#include "sparsebase/reorder/degree_reorder.h" +#include "sparsebase/reorder/reorderer.h" +#include "sparsebase/utils/exception.h" + +using namespace sparsebase; +using namespace sparsebase::reorder; +using namespace sparsebase::bases; +using namespace sparsebase::feature; + +class BandwidthTest : public ::testing::Test { + protected: + feature::Bandwidth feature; + + struct Params1 : sparsebase::utils::Parameters {}; +}; + +TEST_F(BandwidthTest, AllTests) { + sparsebase::context::CPUContext cpu_context; + + Params1 p1; + const int n = 7, m = 7; + const int nnz = 12; + int row_ptr_[n+1] = {0, 2, 2, 5, 7, 9, 11, 12}; + int col_[nnz] = {2, 3, 0, 3, 4, 0, 2, 2, 5, 4, 6, 5}; + /* 0 1 2 3 4 5 6 + * 0 0 0 1 1 0 0 0 + * 1 0 0 0 0 0 0 0 + * 2 1 0 0 1 1 0 0 + * 3 1 0 1 0 0 0 0 + * 4 0 0 1 0 0 1 0 + * 5 0 0 0 0 1 0 1 + * 6 0 0 0 0 0 1 0 */ + + int ans = 4; + auto csr = new sparsebase::format::CSR(n, m, row_ptr_, col_, nullptr, + sparsebase::format::kOwned); + + // test get_sub_ids + EXPECT_EQ(feature.get_sub_ids().size(), 1); + EXPECT_EQ(feature.get_sub_ids()[0], std::type_index(typeid(feature))); + + // Test get_subs + auto subs = feature.get_subs(); + // a single sub-feature + EXPECT_EQ(subs.size(), 1); + // same type as feature but different address + auto &feat = *(subs[0]); + EXPECT_EQ(std::type_index(typeid(feat)), std::type_index(typeid(feature))); + EXPECT_NE(subs[0], &feature); + + // Check GetBandwidthCSR implementation function + int* bandwidth = + feature::Bandwidth::GetBandwidthCSR({csr}, &p1); + + EXPECT_EQ(*bandwidth, ans); + delete bandwidth; + + // Check GetBandwidth + bandwidth = feature.GetBandwidth(csr, {&cpu_context}, true); + EXPECT_EQ(*bandwidth, ans); + delete bandwidth; + + bandwidth = feature.GetBandwidth(csr, {&cpu_context}, false); + EXPECT_EQ(*bandwidth, ans); + delete bandwidth; + + // Check Extract + auto feature_map = feature.Extract(csr, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + + EXPECT_EQ(*std::any_cast(feature_map[feature.get_id()]), ans); +} \ No newline at end of file diff --git a/tests/suites/sparsebase/feature/coefficient_of_variation_degree_column_tests.cc b/tests/suites/sparsebase/feature/coefficient_of_variation_degree_column_tests.cc new file mode 100644 index 00000000..e42d3aff --- /dev/null +++ b/tests/suites/sparsebase/feature/coefficient_of_variation_degree_column_tests.cc @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "sparsebase/bases/reorder_base.h" +#include "sparsebase/context/context.h" +#include "sparsebase/feature/coefficient_of_variation_degree_column.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/format/format_order_one.h" +#include "sparsebase/format/format_order_two.h" +#include "sparsebase/reorder/degree_reorder.h" +#include "sparsebase/reorder/reorderer.h" +#include "sparsebase/utils/exception.h" + +using namespace sparsebase; +using namespace sparsebase::reorder; +using namespace sparsebase::bases; +using namespace sparsebase::feature; +#include "../functionality_common.inc" + +class CoefficientOfVariationDegreeColumnTest : public ::testing::Test { + protected: + CoefficientOfVariationDegreeColumn feature; + + struct Params1 : sparsebase::utils::Parameters {}; + struct Params2 : sparsebase::utils::Parameters {}; +}; + +TEST_F(CoefficientOfVariationDegreeColumnTest, AllTests) { + // test get_sub_ids + EXPECT_EQ(feature.get_sub_ids().size(), 1); + EXPECT_EQ(feature.get_sub_ids()[0], std::type_index(typeid(feature))); + + // Test get_subs + auto subs = feature.get_subs(); + // a single sub-feature + EXPECT_EQ(subs.size(), 1); + // same type as feature but different address + auto &feat = *(subs[0]); + EXPECT_EQ(std::type_index(typeid(feat)), std::type_index(typeid(feature))); + EXPECT_NE(subs[0], &feature); + + // Check GetCoefficientOfVariationDegreeColumncsc implementation function + Params1 p1; + auto coefficient_of_variation_degree = + CoefficientOfVariationDegreeColumn::GetCoefficientOfVariationDegreeColumnCSC( + {&global_csc}, &p1); + + int degree_sum = col_ptr[n] - col_ptr[0]; + auto avg_in_degrees = degree_sum / (float) n; + auto standard_deviation_in_degrees = 0.0; + for (int i = 0; i < n; i++) { + standard_deviation_in_degrees += (col_ptr[i + 1] - col_ptr[i] - avg_in_degrees)*(col_ptr[i + 1] - col_ptr[i] - avg_in_degrees); + } + auto coefficient_of_variation_in_degrees = sqrt(standard_deviation_in_degrees) / avg_in_degrees; + + EXPECT_NEAR(*coefficient_of_variation_degree, coefficient_of_variation_in_degrees, 0.000001); + + delete coefficient_of_variation_degree; + //// Check GetCoefficientOfVariationDegreeColumn (function matcher) + coefficient_of_variation_degree = + feature.GetCoefficientOfVariationDegreeColumn(&global_csc, {&cpu_context}, true); + EXPECT_NEAR(*coefficient_of_variation_degree, coefficient_of_variation_in_degrees, 0.000001); + delete coefficient_of_variation_degree; + + coefficient_of_variation_degree = + feature.GetCoefficientOfVariationDegreeColumn(&global_csc, {&cpu_context}, false); + EXPECT_NEAR(*coefficient_of_variation_degree, coefficient_of_variation_in_degrees, 0.000001); + delete coefficient_of_variation_degree; + + // Check GetCoefficientOfVariationDegreeColumn with conversion + coefficient_of_variation_degree = + feature.GetCoefficientOfVariationDegreeColumn(&global_coo, {&cpu_context}, true); + EXPECT_NEAR(*coefficient_of_variation_degree, coefficient_of_variation_in_degrees, 0.000001); + delete coefficient_of_variation_degree; + EXPECT_THROW(feature.GetCoefficientOfVariationDegreeColumn(&global_coo, {&cpu_context}, false), + utils::DirectExecutionNotAvailableException< + std::vector>); + // Check GetCoefficientOfVariationDegreeColumn with conversion and cached + auto coefficient_of_variation_degree_format = + feature.GetCoefficientOfVariationDegreeColumnCached(&global_coo, {&cpu_context}, true); + EXPECT_NEAR(*std::get<1>(coefficient_of_variation_degree_format), coefficient_of_variation_in_degrees, 0.000001); + delete std::get<1>(coefficient_of_variation_degree_format); + + auto cached_data = std::get<0>(coefficient_of_variation_degree_format); + ASSERT_EQ(cached_data.size(), 1); + ASSERT_EQ(cached_data[0][0]->get_id(), std::type_index(typeid(global_csc))); + auto converted_csc = + cached_data[0][0]->AsAbsolute>(); + compare_csc(&global_csc, converted_csc); + // Check Extract + auto feature_map = feature.Extract(&global_csc, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + EXPECT_NEAR(*std::any_cast(feature_map[feature.get_id()]), + coefficient_of_variation_in_degrees, 0.000001); + + // Check Extract with conversion + feature_map = feature.Extract(&global_coo, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + EXPECT_NEAR(*std::any_cast(feature_map[feature.get_id()]), + coefficient_of_variation_in_degrees, 0.000001); +} \ No newline at end of file diff --git a/tests/suites/sparsebase/feature/geometric_avg_degree_column_tests.cc b/tests/suites/sparsebase/feature/geometric_avg_degree_column_tests.cc new file mode 100644 index 00000000..2cca19ba --- /dev/null +++ b/tests/suites/sparsebase/feature/geometric_avg_degree_column_tests.cc @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "sparsebase/bases/reorder_base.h" +#include "sparsebase/context/context.h" +#include "sparsebase/feature/geometric_avg_degree_column.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/format/format_order_one.h" +#include "sparsebase/format/format_order_two.h" +#include "sparsebase/reorder/degree_reorder.h" +#include "sparsebase/reorder/reorderer.h" +#include "sparsebase/utils/exception.h" + +using namespace sparsebase; +using namespace sparsebase::reorder; +using namespace sparsebase::bases; +using namespace sparsebase::feature; +#include "../functionality_common.inc" + +class GeometricAvgDegreeColumnTest : public ::testing::Test { + protected: + GeometricAvgDegreeColumn feature; + + struct Params1 : sparsebase::utils::Parameters {}; + struct Params2 : sparsebase::utils::Parameters {}; +}; + +TEST_F(GeometricAvgDegreeColumnTest, AllTests) { + // test get_sub_ids + EXPECT_EQ(feature.get_sub_ids().size(), 1); + EXPECT_EQ(feature.get_sub_ids()[0], std::type_index(typeid(feature))); + + // Test get_subs + auto subs = feature.get_subs(); + // a single sub-feature + EXPECT_EQ(subs.size(), 1); + // same type as feature but different address + auto &feat = *(subs[0]); + EXPECT_EQ(std::type_index(typeid(feat)), std::type_index(typeid(feature))); + EXPECT_NE(subs[0], &feature); + + // Check GetGeometricAvgDegreeColumncsc implementation function + Params1 p1; + auto geometric_avg_degree = + GeometricAvgDegreeColumn::GetGeometricAvgDegreeColumnCSC( + {&global_csc}, &p1); + + auto geometric_avg_in_degrees = 0.0; + for (int i = 0; i < n; i++) { + geometric_avg_in_degrees += log(col_ptr[i + 1] - col_ptr[i]); + } + geometric_avg_in_degrees = exp(geometric_avg_in_degrees / n); + + EXPECT_NEAR(*geometric_avg_degree, geometric_avg_in_degrees, 0.000001); + + delete geometric_avg_degree; + //// Check GetGeometricAvgDegreeColumn (function matcher) + geometric_avg_degree = + feature.GetGeometricAvgDegreeColumn(&global_csc, {&cpu_context}, true); + EXPECT_NEAR(*geometric_avg_degree, geometric_avg_in_degrees, 0.000001); + delete geometric_avg_degree; + + geometric_avg_degree = + feature.GetGeometricAvgDegreeColumn(&global_csc, {&cpu_context}, false); + EXPECT_NEAR(*geometric_avg_degree, geometric_avg_in_degrees, 0.000001); + delete geometric_avg_degree; + + // Check GetGeometricAvgDegreeColumn with conversion + geometric_avg_degree = + feature.GetGeometricAvgDegreeColumn(&global_coo, {&cpu_context}, true); + EXPECT_NEAR(*geometric_avg_degree, geometric_avg_in_degrees, 0.000001); + delete geometric_avg_degree; + EXPECT_THROW(feature.GetGeometricAvgDegreeColumn(&global_coo, {&cpu_context}, false), + utils::DirectExecutionNotAvailableException< + std::vector>); + // Check GetGeometricAvgDegreeColumn with conversion and cached + auto geometric_avg_degree_format = + feature.GetGeometricAvgDegreeColumnCached(&global_coo, {&cpu_context}, true); + EXPECT_NEAR(*std::get<1>(geometric_avg_degree_format), geometric_avg_in_degrees, 0.000001); + delete std::get<1>(geometric_avg_degree_format); + + auto cached_data = std::get<0>(geometric_avg_degree_format); + ASSERT_EQ(cached_data.size(), 1); + ASSERT_EQ(cached_data[0][0]->get_id(), std::type_index(typeid(global_csc))); + auto converted_csc = + cached_data[0][0]->AsAbsolute>(); + compare_csc(&global_csc, converted_csc); + // Check Extract + auto feature_map = feature.Extract(&global_csc, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + EXPECT_NEAR(*std::any_cast(feature_map[feature.get_id()]), + geometric_avg_in_degrees, 0.000001); + + // Check Extract with conversion + feature_map = feature.Extract(&global_coo, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + EXPECT_NEAR(*std::any_cast(feature_map[feature.get_id()]), + geometric_avg_in_degrees, 0.000001); +} \ No newline at end of file diff --git a/tests/suites/sparsebase/feature/max_degree_column_tests.cc b/tests/suites/sparsebase/feature/max_degree_column_tests.cc new file mode 100644 index 00000000..721464a3 --- /dev/null +++ b/tests/suites/sparsebase/feature/max_degree_column_tests.cc @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "sparsebase/bases/reorder_base.h" +#include "sparsebase/context/context.h" +#include "sparsebase/feature/max_degree_column.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/format/format_order_one.h" +#include "sparsebase/format/format_order_two.h" +#include "sparsebase/reorder/degree_reorder.h" +#include "sparsebase/reorder/reorderer.h" +#include "sparsebase/utils/exception.h" + +using namespace sparsebase; + +using namespace sparsebase::reorder; +using namespace sparsebase::bases; +using namespace sparsebase::feature; +#include "../functionality_common.inc" + +class MaxDegreeColumnTest : public ::testing::Test { + protected: + feature::MaxDegreeColumn feature; + + struct Params1 : sparsebase::utils::Parameters {}; + struct Params2 : sparsebase::utils::Parameters {}; +}; + +TEST_F(MaxDegreeColumnTest, AllTests) { + // test get_sub_ids + EXPECT_EQ(feature.get_sub_ids().size(), 1); + EXPECT_EQ(feature.get_sub_ids()[0], std::type_index(typeid(feature))); + + // Test get_subs + auto subs = feature.get_subs(); + // a single sub-feature + EXPECT_EQ(subs.size(), 1); + // same type as feature but different address + auto &feat = *(subs[0]); + EXPECT_EQ(std::type_index(typeid(feat)), std::type_index(typeid(feature))); + EXPECT_NE(subs[0], &feature); + + // Check GetMaxDegreeCSR implementation function + Params1 p1; + auto max_degree = + feature::MaxDegreeColumn::GetMaxDegreeColumnCSC({&global_csc}, &p1); + + auto max_in_degrees = 0; + for (int i = 0; i < n; ++i) + max_in_degrees = std::max(max_in_degrees, degrees[i]); + EXPECT_EQ(*max_degree, max_in_degrees); + delete max_degree; + // Check GetMaxDegree + max_degree = feature.GetMaxDegreeColumn(&global_csc, {&cpu_context}, true); + EXPECT_EQ(*max_degree, max_in_degrees); + delete max_degree; + + max_degree = feature.GetMaxDegreeColumn(&global_csc, {&cpu_context}, false); + EXPECT_EQ(*max_degree, max_in_degrees); + delete max_degree; + + // Check GetMaxDegree with conversion + max_degree = feature.GetMaxDegreeColumn(&global_coo, {&cpu_context}, true); + EXPECT_EQ(*max_degree, max_in_degrees); + + EXPECT_THROW(feature.GetMaxDegreeColumn(&global_coo, {&cpu_context}, false), + utils::DirectExecutionNotAvailableException< + std::vector>); + // Check Extract + auto feature_map = feature.Extract(&global_csc, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + + EXPECT_EQ(*std::any_cast(feature_map[feature.get_id()]), max_in_degrees); + + // Check Extract with conversion + feature_map = feature.Extract(&global_coo, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + + EXPECT_EQ(*std::any_cast(feature_map[feature.get_id()]), max_in_degrees); + + EXPECT_THROW(feature.Extract(&global_coo, {&cpu_context}, false), + utils::DirectExecutionNotAvailableException< + std::vector>); + delete max_degree; +} diff --git a/tests/suites/sparsebase/feature/median_degree_column_tests.cc b/tests/suites/sparsebase/feature/median_degree_column_tests.cc new file mode 100644 index 00000000..04a7329c --- /dev/null +++ b/tests/suites/sparsebase/feature/median_degree_column_tests.cc @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "sparsebase/bases/reorder_base.h" +#include "sparsebase/context/context.h" +#include "sparsebase/feature/median_degree_column.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/format/format_order_one.h" +#include "sparsebase/format/format_order_two.h" +#include "sparsebase/reorder/degree_reorder.h" +#include "sparsebase/reorder/reorderer.h" +#include "sparsebase/utils/exception.h" + +using namespace sparsebase; +using namespace sparsebase::reorder; +using namespace sparsebase::bases; +using namespace sparsebase::feature; +#include "../functionality_common.inc" + +class MedianDegreeColumnTest : public ::testing::Test { + protected: + MedianDegreeColumn feature; + + struct Params1 : sparsebase::utils::Parameters {}; + struct Params2 : sparsebase::utils::Parameters {}; +}; + +TEST_F(MedianDegreeColumnTest, AllTests) { + // test get_sub_ids + EXPECT_EQ(feature.get_sub_ids().size(), 1); + EXPECT_EQ(feature.get_sub_ids()[0], std::type_index(typeid(feature))); + + // Test get_subs + auto subs = feature.get_subs(); + // a single sub-feature + EXPECT_EQ(subs.size(), 1); + // same type as feature but different address + auto &feat = *(subs[0]); + EXPECT_EQ(std::type_index(typeid(feat)), std::type_index(typeid(feature))); + EXPECT_NE(subs[0], &feature); + + // Check GetMedianDegreeColumnCSC implementation function + Params1 p1; + auto median_degree = + MedianDegreeColumn::GetMedianDegreeColumnCSC( + {&global_csc}, &p1); + + std::vector test_degrees; + test_degrees.reserve(n); + for (int i = 0; i < n; i++) { + int current_degree = cols[i + 1] - cols[i]; + test_degrees.push_back(current_degree); + } + std::sort(test_degrees.begin(), test_degrees.end()); + + auto median_in_degrees = 0.0; + if (n % 2 == 0) { + median_in_degrees = (test_degrees[n / 2 - 1] + test_degrees[n / 2]) / 2.0; + } + else { + median_in_degrees = test_degrees[n / 2]; + } +/* + //EXPECT_NEAR(*median_degree, median_in_degrees, 0.000001);aaaa + + delete median_degree; + //// Check GetMedianDegree (function matcher) + median_degree = + feature.GetMedianDegreeColumn(&global_csc, {&cpu_context}, true); + EXPECT_NEAR(*median_degree, median_in_degrees, 0.000001); + delete median_degree; + + median_degree = + feature.GetMedianDegreeColumn(&global_csc, {&cpu_context}, false); + EXPECT_NEAR(*median_degree, median_in_degrees, 0.000001); + delete median_degree; + + // Check GetMedianDegree with conversion + median_degree = + feature.GetMedianDegreeColumn(&global_coo, {&cpu_context}, true); + EXPECT_NEAR(*median_degree, median_in_degrees, 0.000001); + delete median_degree; + EXPECT_THROW(feature.GetMedianDegreeColumn(&global_coo, {&cpu_context}, false), + utils::DirectExecutionNotAvailableException< + std::vector>); + // Check GetMedianDegree with conversion and cached + auto median_degree_format = + feature.GetMedianDegreeColumnCached(&global_coo, {&cpu_context}, true); + EXPECT_NEAR(*std::get<1>(median_degree_format), median_in_degrees, 0.000001); + delete std::get<1>(median_degree_format); + + auto cached_data = std::get<0>(median_degree_format); + ASSERT_EQ(cached_data.size(), 1); + ASSERT_EQ(cached_data[0][0]->get_id(), std::type_index(typeid(global_csc))); + auto converted_csc = + cached_data[0][0]->AsAbsolute>(); + compare_csc(&global_csc, converted_csc); + // Check Extract + auto feature_map = feature.Extract(&global_csc, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + EXPECT_NEAR(*std::any_cast(feature_map[feature.get_id()]), + median_in_degrees, 0.000001); + + // Check Extract with conversion + feature_map = feature.Extract(&global_coo, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + EXPECT_NEAR(*std::any_cast(feature_map[feature.get_id()]), + median_in_degrees, 0.000001); + */ +} diff --git a/tests/suites/sparsebase/feature/min_degree_column_tests.cc b/tests/suites/sparsebase/feature/min_degree_column_tests.cc new file mode 100644 index 00000000..cc17b2bb --- /dev/null +++ b/tests/suites/sparsebase/feature/min_degree_column_tests.cc @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "sparsebase/bases/reorder_base.h" +#include "sparsebase/context/context.h" +#include "sparsebase/feature/min_degree_column.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/format/format_order_one.h" +#include "sparsebase/format/format_order_two.h" +#include "sparsebase/reorder/degree_reorder.h" +#include "sparsebase/reorder/reorderer.h" +#include "sparsebase/utils/exception.h" + +using namespace sparsebase; +using namespace sparsebase::reorder; +using namespace sparsebase::bases; +using namespace sparsebase::feature; +#include "../functionality_common.inc" + +class MinDegreeColumnTest : public ::testing::Test { + protected: + feature::MinDegreeColumn feature; + + struct Params1 : sparsebase::utils::Parameters {}; + struct Params2 : sparsebase::utils::Parameters {}; +}; + +TEST_F(MinDegreeColumnTest, AllTests) { + // test get_sub_ids + EXPECT_EQ(feature.get_sub_ids().size(), 1); + EXPECT_EQ(feature.get_sub_ids()[0], std::type_index(typeid(feature))); + + // Test get_subs + auto subs = feature.get_subs(); + // a single sub-feature + EXPECT_EQ(subs.size(), 1); + // same type as feature but different address + auto &feat = *(subs[0]); + EXPECT_EQ(std::type_index(typeid(feat)), std::type_index(typeid(feature))); + EXPECT_NE(subs[0], &feature); + + // Check GetMinDegreeColumnCSC implementation function + Params1 p1; + auto min_degree = + feature::MinDegreeColumn::GetMinDegreeColumnCSC({&global_csc}, &p1); + + auto min_in_degrees = (int)1e9; + for (int i = 0; i < n; ++i) + min_in_degrees = std::min(min_in_degrees, degrees[i]); + EXPECT_EQ(*min_degree, min_in_degrees); + delete min_degree; + // Check GetMinDegree + min_degree = feature.GetMinDegreeColumn(&global_csc, {&cpu_context}, true); + EXPECT_EQ(*min_degree, min_in_degrees); + delete min_degree; + + min_degree = feature.GetMinDegreeColumn(&global_csc, {&cpu_context}, false); + EXPECT_EQ(*min_degree, min_in_degrees); + delete min_degree; + + // Check GetMinDegree with conversion + min_degree = feature.GetMinDegreeColumn(&global_coo, {&cpu_context}, true); + EXPECT_EQ(*min_degree, min_in_degrees); + + EXPECT_THROW(feature.GetMinDegreeColumn(&global_coo, {&cpu_context}, false), + utils::DirectExecutionNotAvailableException< + std::vector>); + // Check Extract + auto feature_map = feature.Extract(&global_csc, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + + EXPECT_EQ(*std::any_cast(feature_map[feature.get_id()]), min_in_degrees); + + // Check Extract with conversion + feature_map = feature.Extract(&global_coo, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + + EXPECT_EQ(*std::any_cast(feature_map[feature.get_id()]), min_in_degrees); + + EXPECT_THROW(feature.Extract(&global_coo, {&cpu_context}, false), + utils::DirectExecutionNotAvailableException< + std::vector>); + delete min_degree; +} + diff --git a/tests/suites/sparsebase/feature/off_diag_block_nnz_tests.cc b/tests/suites/sparsebase/feature/off_diag_block_nnz_tests.cc new file mode 100644 index 00000000..59cdd26c --- /dev/null +++ b/tests/suites/sparsebase/feature/off_diag_block_nnz_tests.cc @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "sparsebase/bases/reorder_base.h" +#include "sparsebase/context/context.h" +#include "sparsebase/feature/off_diag_block_nnz.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/format/format_order_one.h" +#include "sparsebase/format/format_order_two.h" +#include "sparsebase/reorder/degree_reorder.h" +#include "sparsebase/reorder/reorderer.h" +#include "sparsebase/utils/exception.h" + +using namespace sparsebase; +using namespace sparsebase::reorder; +using namespace sparsebase::bases; +using namespace sparsebase::feature; + +class OffDiagBlockNNZTest : public ::testing::Test { +}; + +TEST_F(OffDiagBlockNNZTest, AllTests) { + sparsebase::context::CPUContext cpu_context; + OffDiagBlockNNZParams p1(3); + auto feature = feature::OffDiagBlockNNZ(p1); + const int n = 7, m = 7; + const int nnz = 12; + int row_ptr_[n+1] = {0, 2, 2, 5, 7, 9, 11, 12}; + int col_[nnz] = {2, 3, 0, 3, 4, 0, 2, 2, 5, 4, 6, 5}; + /* 0 1 2 3 4 5 6 + * 0 0 0 1 1 0 0 0 + * 1 0 0 0 0 0 0 0 + * 2 1 0 0 1 1 0 0 + * 3 1 0 1 0 0 0 0 + * 4 0 0 1 0 0 1 0 + * 5 0 0 0 0 1 0 1 + * 6 0 0 0 0 0 1 0 */ + + int ans = 8; + auto csr = new sparsebase::format::CSR(n, m, row_ptr_, col_, nullptr, + sparsebase::format::kOwned); + // test get_sub_ids + EXPECT_EQ(feature.get_sub_ids().size(), 1); + EXPECT_EQ(feature.get_sub_ids()[0], std::type_index(typeid(feature))); + + // Test get_subs + auto subs = feature.get_subs(); + // a single sub-feature + EXPECT_EQ(subs.size(), 1); + // same type as feature but different address + auto &feat = *(subs[0]); + EXPECT_EQ(std::type_index(typeid(feat)), std::type_index(typeid(feature))); + EXPECT_NE(subs[0], &feature); + + // Check GetOffDiagBlockNNZCSR implementation function + int* offdiagblocknnz = + feature::OffDiagBlockNNZ::GetOffDiagBlockNNZCSR({csr}, &p1); + + EXPECT_EQ(*offdiagblocknnz, ans); + delete offdiagblocknnz; + + // Check GetOffDiagBlockNNZ + offdiagblocknnz = feature.GetOffDiagBlockNNZ(csr, {&cpu_context}, true); + EXPECT_EQ(*offdiagblocknnz, ans); + delete offdiagblocknnz; + + offdiagblocknnz = feature.GetOffDiagBlockNNZ(csr, {&cpu_context}, false); + EXPECT_EQ(*offdiagblocknnz, ans); + delete offdiagblocknnz; + + // Check Extract + auto feature_map = feature.Extract(csr, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + + EXPECT_EQ(*std::any_cast(feature_map[feature.get_id()]), ans); +} diff --git a/tests/suites/sparsebase/feature/profile_tests.cc b/tests/suites/sparsebase/feature/profile_tests.cc new file mode 100644 index 00000000..3cac0eeb --- /dev/null +++ b/tests/suites/sparsebase/feature/profile_tests.cc @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "sparsebase/bases/reorder_base.h" +#include "sparsebase/context/context.h" +#include "sparsebase/feature/profile.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/format/format_order_one.h" +#include "sparsebase/format/format_order_two.h" +#include "sparsebase/reorder/degree_reorder.h" +#include "sparsebase/reorder/reorderer.h" +#include "sparsebase/utils/exception.h" + +using namespace sparsebase; +using namespace sparsebase::reorder; +using namespace sparsebase::bases; +using namespace sparsebase::feature; + +class ProfileTest : public ::testing::Test { + protected: + feature::Profile feature; + + struct Params1 : sparsebase::utils::Parameters {}; +}; + +TEST_F(ProfileTest, AllTests) { + sparsebase::context::CPUContext cpu_context; + Params1 p1; + const int n = 7, m = 7; + const int nnz = 12; + int row_ptr_[n+1] = {0, 2, 2, 5, 7, 9, 11, 12}; + int col_[nnz] = {2, 3, 0, 3, 4, 0, 2, 2, 5, 4, 6, 5}; + /* 0 1 2 3 4 5 6 + * 0 0 0 1 1 0 0 0 + * 1 0 0 0 0 0 0 0 + * 2 1 0 0 1 1 0 0 + * 3 1 0 1 0 0 0 0 + * 4 0 0 1 0 0 1 0 + * 5 0 0 0 0 1 0 1 + * 6 0 0 0 0 0 1 0 */ + + int ans = 9; + auto csr = new sparsebase::format::CSR(n, m, row_ptr_, col_, nullptr, + sparsebase::format::kOwned); + // test get_sub_ids + EXPECT_EQ(feature.get_sub_ids().size(), 1); + EXPECT_EQ(feature.get_sub_ids()[0], std::type_index(typeid(feature))); + + // Test get_subs + auto subs = feature.get_subs(); + // a single sub-feature + EXPECT_EQ(subs.size(), 1); + // same type as feature but different address + auto &feat = *(subs[0]); + EXPECT_EQ(std::type_index(typeid(feat)), std::type_index(typeid(feature))); + EXPECT_NE(subs[0], &feature); + + // Check GetProfileCSR implementation function + int* profile = + feature::Profile::GetProfileCSR({csr}, &p1); + + EXPECT_EQ(*profile, ans); + delete profile; + + // Check GetProfile + profile = feature.GetProfile(csr, {&cpu_context}, true); + EXPECT_EQ(*profile, ans); + delete profile; + + profile = feature.GetProfile(csr, {&cpu_context}, false); + EXPECT_EQ(*profile, ans); + delete profile; + + // Check Extract + auto feature_map = feature.Extract(csr, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + + EXPECT_EQ(*std::any_cast(feature_map[feature.get_id()]), ans); +} diff --git a/tests/suites/sparsebase/feature/standard_deviation_degree_column_tests.cc b/tests/suites/sparsebase/feature/standard_deviation_degree_column_tests.cc new file mode 100644 index 00000000..431e9efb --- /dev/null +++ b/tests/suites/sparsebase/feature/standard_deviation_degree_column_tests.cc @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "sparsebase/bases/reorder_base.h" +#include "sparsebase/context/context.h" +#include "sparsebase/feature/standard_deviation_degree_column.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/format/format_order_one.h" +#include "sparsebase/format/format_order_two.h" +#include "sparsebase/reorder/degree_reorder.h" +#include "sparsebase/reorder/reorderer.h" +#include "sparsebase/utils/exception.h" + +using namespace sparsebase; +using namespace sparsebase::reorder; +using namespace sparsebase::bases; +using namespace sparsebase::feature; +#include "../functionality_common.inc" + +class StandardDeviationDegreeColumnTest : public ::testing::Test { + protected: + StandardDeviationDegreeColumn feature; + + struct Params1 : sparsebase::utils::Parameters {}; + struct Params2 : sparsebase::utils::Parameters {}; +}; + +TEST_F(StandardDeviationDegreeColumnTest, AllTests) { + // test get_sub_ids + EXPECT_EQ(feature.get_sub_ids().size(), 1); + EXPECT_EQ(feature.get_sub_ids()[0], std::type_index(typeid(feature))); + + // Test get_subs + auto subs = feature.get_subs(); + // a single sub-feature + EXPECT_EQ(subs.size(), 1); + // same type as feature but different address + auto &feat = *(subs[0]); + EXPECT_EQ(std::type_index(typeid(feat)), std::type_index(typeid(feature))); + EXPECT_NE(subs[0], &feature); + + // Check GetStandardDeviationDegreeColumncsc implementation function + Params1 p1; + auto standard_deviation_degree = + StandardDeviationDegreeColumn::GetStandardDeviationDegreeColumnCSC( + {&global_csc}, &p1); + + int degree_sum = col_ptr[n] - col_ptr[0]; + auto avg_in_degrees = degree_sum / (float) n; + auto standard_deviation_in_degrees = 0.0; + for (int i = 0; i < n; i++) { + standard_deviation_in_degrees += (col_ptr[i + 1] - col_ptr[i] - avg_in_degrees)*(col_ptr[i + 1] - col_ptr[i] - avg_in_degrees); + } + standard_deviation_in_degrees = sqrt(standard_deviation_in_degrees); + + EXPECT_NEAR(*standard_deviation_degree, standard_deviation_in_degrees, 0.000001); + + delete standard_deviation_degree; + //// Check GetStandardDeviationDegreeColumn (function matcher) + standard_deviation_degree = + feature.GetStandardDeviationDegreeColumn(&global_csc, {&cpu_context}, true); + EXPECT_NEAR(*standard_deviation_degree, standard_deviation_in_degrees, 0.000001); + delete standard_deviation_degree; + + standard_deviation_degree = + feature.GetStandardDeviationDegreeColumn(&global_csc, {&cpu_context}, false); + EXPECT_NEAR(*standard_deviation_degree, standard_deviation_in_degrees, 0.000001); + delete standard_deviation_degree; + + // Check GetStandardDeviationDegreeColumn with conversion + standard_deviation_degree = + feature.GetStandardDeviationDegreeColumn(&global_coo, {&cpu_context}, true); + EXPECT_NEAR(*standard_deviation_degree, standard_deviation_in_degrees, 0.000001); + delete standard_deviation_degree; + EXPECT_THROW(feature.GetStandardDeviationDegreeColumn(&global_coo, {&cpu_context}, false), + utils::DirectExecutionNotAvailableException< + std::vector>); + // Check GetStandardDeviationDegreeColumn with conversion and cached + auto standard_deviation_degree_format = + feature.GetStandardDeviationDegreeColumnCached(&global_coo, {&cpu_context}, true); + EXPECT_NEAR(*std::get<1>(standard_deviation_degree_format), standard_deviation_in_degrees, 0.000001); + delete std::get<1>(standard_deviation_degree_format); + + auto cached_data = std::get<0>(standard_deviation_degree_format); + ASSERT_EQ(cached_data.size(), 1); + ASSERT_EQ(cached_data[0][0]->get_id(), std::type_index(typeid(global_csc))); + auto converted_csc = + cached_data[0][0]->AsAbsolute>(); + compare_csc(&global_csc, converted_csc); + // Check Extract + auto feature_map = feature.Extract(&global_csc, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + EXPECT_NEAR(*std::any_cast(feature_map[feature.get_id()]), + standard_deviation_in_degrees, 0.000001); + + // Check Extract with conversion + feature_map = feature.Extract(&global_coo, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + EXPECT_NEAR(*std::any_cast(feature_map[feature.get_id()]), + standard_deviation_in_degrees, 0.000001); +} diff --git a/tests/suites/sparsebase/feature/triangle_count_tests.cc b/tests/suites/sparsebase/feature/triangle_count_tests.cc new file mode 100644 index 00000000..bcb85bdd --- /dev/null +++ b/tests/suites/sparsebase/feature/triangle_count_tests.cc @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "sparsebase/bases/reorder_base.h" +#include "sparsebase/context/context.h" +#include "sparsebase/feature/triangle_count.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/format/format_order_one.h" +#include "sparsebase/format/format_order_two.h" +#include "sparsebase/reorder/degree_reorder.h" +#include "sparsebase/reorder/reorderer.h" +#include "sparsebase/utils/exception.h" + +using namespace sparsebase; +using namespace sparsebase::reorder; +using namespace sparsebase::bases; +using namespace sparsebase::feature; +#include "../functionality_common.inc" + +class TriangleCountTest : public ::testing::Test { +}; + +TEST_F(TriangleCountTest, DirectedTriangleTests) { + const int n_ = 10, m_ = 10; + int row_ptr_[n_+1] = {0, 0, 1, 2, 3, 4, 5, 6, 8, 10, 12}; + int col_[12] = {2, 3, 1, 6, 4, 5, 8, 9, 7, 9, 7, 8}; + /* Triangles + * 1 -> 2 -> 3 -> 1, + * 4 -> 6 -> 5 -> 4, + * 7 -> 8 -> 9 -> 7, + * 7 -> 9 -> 8 -> 7 + */ + int64_t ans = 4; + auto csr = new sparsebase::format::CSR(n_, m_, row_ptr_, col_, nullptr, + sparsebase::format::kOwned); + + TriangleCountParams p_directed(true); + auto feature = feature::TriangleCount(p_directed); + // test get_sub_ids + EXPECT_EQ(feature.get_sub_ids().size(), 1); + EXPECT_EQ(feature.get_sub_ids()[0], std::type_index(typeid(feature))); + + // Test get_subs + auto subs = feature.get_subs(); + // a single sub-feature + EXPECT_EQ(subs.size(), 1); + // same type as feature but different address + auto &feat = *(subs[0]); + EXPECT_EQ(std::type_index(typeid(feat)), std::type_index(typeid(feature))); + EXPECT_NE(subs[0], &feature); + + // Check GetTriangleCountCSR implementation function + auto triangle_count = + feature::TriangleCount::GetTriangleCountCSR({csr}, &p_directed); + + EXPECT_EQ(*triangle_count, ans); + delete triangle_count; + // Check GetTriangleCount + triangle_count = feature.GetTriangleCount(csr, {&cpu_context}, true); + EXPECT_EQ(*triangle_count, ans); + delete triangle_count; + + triangle_count = feature.GetTriangleCount(csr, {&cpu_context}, false); + EXPECT_EQ(*triangle_count, ans); + delete triangle_count; + + // Check GetTriangleCount with conversion + triangle_count = feature.GetTriangleCount(csr, {&cpu_context}, true); + EXPECT_EQ(*triangle_count, ans); + delete triangle_count; + + // Check Extract + auto feature_map = feature.Extract(csr, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + + EXPECT_EQ(*std::any_cast(feature_map[feature.get_id()]), ans); +} + +TEST_F(TriangleCountTest, UndirectedTriangleTests) { + const int n_ = 10, m_ = 10; + int row_ptr_[n_+1] = {0, 0, 2, 4, 6, 8, 10, 12, 13, 15, 16}; + int col_[16] = {2, 3, 1, 3, 1, 2, 5, 6, 4, 6, 4, 5, 8, 7, 9, 8}; + /* Triangles + * (1, 2, 3) + * (4, 6, 5) + */ + int64_t ans = 2; + auto csr = new sparsebase::format::CSR(n_, m_, row_ptr_, col_, nullptr, + sparsebase::format::kOwned); + + TriangleCountParams p_undirected(false); + auto feature = feature::TriangleCount(p_undirected); + // test get_sub_ids + EXPECT_EQ(feature.get_sub_ids().size(), 1); + EXPECT_EQ(feature.get_sub_ids()[0], std::type_index(typeid(feature))); + + // Test get_subs + auto subs = feature.get_subs(); + // a single sub-feature + EXPECT_EQ(subs.size(), 1); + // same type as feature but different address + auto &feat = *(subs[0]); + EXPECT_EQ(std::type_index(typeid(feat)), std::type_index(typeid(feature))); + EXPECT_NE(subs[0], &feature); + + // Check GetTriangleCountCSR implementation function + auto triangle_count = + feature::TriangleCount::GetTriangleCountCSR({csr}, &p_undirected); + + EXPECT_EQ(*triangle_count, ans); + delete triangle_count; + // Check GetTriangleCount + triangle_count = feature.GetTriangleCount(csr, {&cpu_context}, true); + EXPECT_EQ(*triangle_count, ans); + delete triangle_count; + + triangle_count = feature.GetTriangleCount(csr, {&cpu_context}, false); + EXPECT_EQ(*triangle_count, ans); + delete triangle_count; + + // Check GetTriangleCount with conversion + triangle_count = feature.GetTriangleCount(csr, {&cpu_context}, true); + EXPECT_EQ(*triangle_count, ans); + delete triangle_count; + + // Check Extract + auto feature_map = feature.Extract(csr, {&cpu_context}, true); + // Check map size and type + EXPECT_EQ(feature_map.size(), 1); + for (auto feat : feature_map) { + EXPECT_EQ(feat.first, std::type_index(typeid(feature))); + } + + EXPECT_EQ(*std::any_cast(feature_map[feature.get_id()]), ans); +} \ No newline at end of file diff --git a/tests/suites/sparsebase/functionality_common.inc b/tests/suites/sparsebase/functionality_common.inc index d1af120b..ffd8177d 100644 --- a/tests/suites/sparsebase/functionality_common.inc +++ b/tests/suites/sparsebase/functionality_common.inc @@ -2,9 +2,13 @@ #include "sparsebase/format/array.h" #include "sparsebase/format/csr.h" #include "sparsebase/format/coo.h" +#include "sparsebase/format/csc.h" const int n = 3; const int nnz = 4; int row_ptr[n + 1] = {0, 2, 3, 4}; +int col_ptr[n + 1] = {0, 2, 3, 4}; +int row_ind[nnz] = {1, 2, 0, 0}; +int vals_csr[nnz] = {3, 4, 1, 2}; int cols[nnz] = {1, 2, 0, 0}; int rows[nnz] = {0, 0, 1, 2}; float distribution[n] = {2.0 / nnz, 1.0 / nnz, 1.0 / nnz}; @@ -54,6 +58,8 @@ format::Array orig_arr(n, original_array, format::kNotOwned); format::Array inv_arr(n, reordered_array, format::kNotOwned); format::CSR global_csr(n, n, row_ptr, cols, vals, format::kNotOwned); +format::CSC global_csc(n, n, col_ptr, row_ind, vals_csr, + format::kNotOwned); format::COO global_coo(n, n, nnz, rows, cols, vals, format::kNotOwned); sparsebase::context::CPUContext cpu_context; @@ -114,6 +120,21 @@ void compare_csr(format::CSR *correct, EXPECT_EQ(correct_col[i], testing_col[i]); } } +template +void compare_csc(format::CSC *correct, + format::CSC *testing) { + auto correct_col_ptr = correct->get_col_ptr(); + auto correct_row = correct->get_row(); + auto testing_col_ptr = testing->get_col_ptr(); + auto testing_row = testing->get_row(); + + for (int i = 0; i < nnz; i++) { + EXPECT_EQ(correct_col_ptr[i], testing_col_ptr[i]); + } + for (int i = 0; i < nnz; i++) { + EXPECT_EQ(correct_row[i], testing_row[i]); + } +} template void confirm_renumbered_csr(V *xadj, V *renumbered_xadj, E *adj, E *renumbered_adj, O *inverse_order, L n) { diff --git a/tests/suites/sparsebase/io/CMakeLists.txt b/tests/suites/sparsebase/io/CMakeLists.txt index 9aec4418..10ced3b3 100644 --- a/tests/suites/sparsebase/io/CMakeLists.txt +++ b/tests/suites/sparsebase/io/CMakeLists.txt @@ -69,3 +69,43 @@ add_executable(sparsebase_io_edge_list_writer_tests.test edge_list_writer_tests. target_link_libraries(sparsebase_io_edge_list_writer_tests.test sparsebase) target_link_libraries(sparsebase_io_edge_list_writer_tests.test gtest gtest_main) add_test(NAME sparsebase_io_edge_list_writer_tests.test COMMAND sparsebase_io_edge_list_writer_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(mtx_writer_tests.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_io_mtx_writer_tests.test mtx_writer_tests.cc) +target_link_libraries(sparsebase_io_mtx_writer_tests.test sparsebase) +target_link_libraries(sparsebase_io_mtx_writer_tests.test gtest gtest_main) +add_test(NAME sparsebase_io_mtx_writer_tests.test COMMAND sparsebase_io_mtx_writer_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(metis_graph_reader_tests.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_io_metis_graph_reader_tests.test metis_graph_reader_tests.cc) +target_link_libraries(sparsebase_io_metis_graph_reader_tests.test sparsebase) +target_link_libraries(sparsebase_io_metis_graph_reader_tests.test gtest gtest_main) +add_test(NAME sparsebase_io_metis_graph_reader_tests.test COMMAND sparsebase_io_metis_graph_reader_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(metis_graph_writer_tests.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_io_metis_graph_writer_tests.test metis_graph_writer_tests.cc) +target_link_libraries(sparsebase_io_metis_graph_writer_tests.test sparsebase) +target_link_libraries(sparsebase_io_metis_graph_writer_tests.test gtest gtest_main) +add_test(NAME sparsebase_io_metis_graph_writer_tests.test COMMAND sparsebase_io_metis_graph_writer_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(patoh_reader_tests.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_io_patoh_reader_tests.test patoh_reader_tests.cc) +target_link_libraries(sparsebase_io_patoh_reader_tests.test sparsebase) +target_link_libraries(sparsebase_io_patoh_reader_tests.test gtest gtest_main) +add_test(NAME sparsebase_io_patoh_reader_tests.test COMMAND sparsebase_io_patoh_reader_tests.test) + +if(${USE_CUDA}) + set_source_files_properties(patoh_writer_tests.cc PROPERTIES LANGUAGE CUDA) +endif() +add_executable(sparsebase_io_patoh_writer_tests.test patoh_writer_tests.cc) +target_link_libraries(sparsebase_io_patoh_writer_tests.test sparsebase) +target_link_libraries(sparsebase_io_patoh_writer_tests.test gtest gtest_main) +add_test(NAME sparsebase_io_patoh_writer_tests.test COMMAND sparsebase_io_patoh_writer_tests.test) \ No newline at end of file diff --git a/tests/suites/sparsebase/io/metis_graph_reader_tests.cc b/tests/suites/sparsebase/io/metis_graph_reader_tests.cc new file mode 100644 index 00000000..10e581cd --- /dev/null +++ b/tests/suites/sparsebase/io/metis_graph_reader_tests.cc @@ -0,0 +1,208 @@ +#include +#include +#include "gtest/gtest.h" +#include "reader_data.inc" +#include "sparsebase/sparsebase.h" +#include "sparsebase/object/object.h" +#include "sparsebase/io/metis_graph_reader.h" +TEST(MetisGraphReader, ReadGraph) { + //Write the metis graph data with vertex & edge weights to a file + std::ofstream ofs("vertex_edge_weights.graph"); + ofs << metis_graph_1; + ofs.close(); + + //Write the metis graph data with multiple vertex weights to a file + std::ofstream ofs2("multiple_vertex_weights.graph"); + ofs2 << metis_graph_2; + ofs2.close(); + + //File 1 + //Read it using sparsebase + sparsebase::io::MetisGraphReader reader1("vertex_edge_weights.graph", false); + sparsebase::object::Graph* graph1 = reader1.ReadGraph(); + sparsebase::format::Format *con1 = graph1->get_connectivity(); + int n1 = con1->get_dimensions()[0]; + int m1 = con1->get_num_nnz(); + auto coo1 = con1->AsAbsolute>(); + auto row1 = coo1->get_row(); + auto col1 = coo1->get_col(); + auto edgeWeights1 = coo1->get_vals(); + auto vertexWeights1 = graph1->vertexWeights_; + //Check the dimensions + EXPECT_EQ(n1, metis_n_1 + 1); //1 indexed + EXPECT_EQ(m1, metis_m_1); + EXPECT_EQ(graph1->ncon_, metis_ncon_1); + EXPECT_NE(row1, nullptr); + EXPECT_NE(col1, nullptr); + EXPECT_NE(edgeWeights1, nullptr); + EXPECT_NE(vertexWeights1, nullptr); + + //Check edge weights + for (int i = 0; i < m1; ++i) + EXPECT_EQ(metis_val_1[i], edgeWeights1[i]); + + //Check vertex weights + for (int vertex = 0; vertex < n1; ++vertex) { + auto weight = vertexWeights1[vertex]->get_vals()[0]; + EXPECT_EQ(metis_vertex_weights_1[vertex], weight); + } + + //Check if all edges read correctly + std::set> edge_set1; + for (int i = 0; i < metis_m_1; ++i) { + edge_set1.emplace(metis_row_1[i], metis_col_1[i]); + } + for (int i = 0; i < m1; i++) { + std::pair p(row1[i], col1[i]); + EXPECT_NE(edge_set1.find(p), edge_set1.end()); + } + + //File 2 + //Read it using sparsebase + sparsebase::io::MetisGraphReader reader2("multiple_vertex_weights.graph", false); + sparsebase::object::Graph* graph2 = reader2.ReadGraph(); + sparsebase::format::Format *con2 = graph2->get_connectivity(); + int n2 = con2->get_dimensions()[0]; + int m2 = con2->get_num_nnz(); + auto coo2 = con2->AsAbsolute>(); + auto row2 = coo2->get_row(); + auto col2 = coo2->get_col(); + auto edgeWeights2 = coo2->get_vals(); + auto vertexWeights2 = graph2->vertexWeights_; + //Check the dimensions + EXPECT_EQ(n2, metis_n_2 + 1); //1 indexed + EXPECT_EQ(m2, metis_m_2); + EXPECT_EQ(graph2->ncon_, metis_ncon_2); + EXPECT_NE(row2, nullptr); + EXPECT_NE(col2, nullptr); + //Graph2 has no edge weights + EXPECT_EQ(edgeWeights2, nullptr); + EXPECT_NE(vertexWeights2, nullptr); + + //Check vertex weights + for (int vertex = 0; vertex < n2; ++vertex) { + auto weights = vertexWeights2[vertex]->get_vals(); + for (int i = 0; i < metis_ncon_2; ++i){ + EXPECT_EQ(metis_vertex_weights_2[vertex][i], weights[i]); + } + } + + //Check if all edges read correctly + std::set> edge_set2; + for (int i = 0; i < metis_m_2; ++i) { + edge_set2.emplace(metis_row_2[i], metis_col_2[i]); + } + for (int i = 0; i < m2; i++) { + std::pair p(row2[i], col2[i]); + EXPECT_NE(edge_set2.find(p), edge_set2.end()); + } + + //File 1 converted to 0 indexed + //Read it using sparsebase + sparsebase::io::MetisGraphReader reader3("vertex_edge_weights.graph", true); + sparsebase::object::Graph* graph3 = reader3.ReadGraph(); + sparsebase::format::Format *con3 = graph3->get_connectivity(); + int n3 = con3->get_dimensions()[0]; + int m3 = con3->get_num_nnz(); + auto coo3 = con3->AsAbsolute>(); + auto row3 = coo3->get_row(); + auto col3 = coo3->get_col(); + auto edgeWeights3 = coo3->get_vals(); + auto vertexWeights3 = graph3->vertexWeights_; + //Check the dimensions + EXPECT_EQ(n3, metis_n_1); //0_indexed + EXPECT_EQ(m3, metis_m_1); + EXPECT_EQ(graph3->ncon_, metis_ncon_1); + EXPECT_NE(row3, nullptr); + EXPECT_NE(col3, nullptr); + EXPECT_NE(edgeWeights3, nullptr); + EXPECT_NE(vertexWeights3, nullptr); + + //Check edge weights + for (int i = 0; i < m3; ++i) + EXPECT_EQ(metis_val_1[i], edgeWeights3[i]); + + //Check vertex weights + for (int vertex = 0; vertex < n3; ++vertex) { + auto weight = vertexWeights3[vertex]->get_vals()[0]; + EXPECT_EQ(metis_vertex_weights_1[vertex + 1], weight); //0 indexed + } + + //Check if all edges read correctly + std::set> edge_set3; + for (int i = 0; i < metis_m_1; ++i) { + edge_set3.emplace(metis_row_1[i] - 1, metis_col_1[i] - 1); //0 indexed + } + for (int i = 0; i < m3; i++) { + std::pair p(row3[i], col3[i]); + EXPECT_NE(edge_set3.find(p), edge_set3.end()); + } + + //File 2 converted to 0 indexed + //Read it using sparsebase + sparsebase::io::MetisGraphReader reader4("multiple_vertex_weights.graph", true); + sparsebase::object::Graph* graph4 = reader4.ReadGraph(); + sparsebase::format::Format *con4 = graph4->get_connectivity(); + int n4 = con4->get_dimensions()[0]; + int m4 = con4->get_num_nnz(); + auto coo4 = con4->AsAbsolute>(); + auto row4 = coo4->get_row(); + auto col4 = coo4->get_col(); + auto edgeWeights4 = coo4->get_vals(); + auto vertexWeights4 = graph4->vertexWeights_; + //Check the dimensions + EXPECT_EQ(n4, metis_n_2); //0 indexed + EXPECT_EQ(m4, metis_m_2); + EXPECT_EQ(graph4->ncon_, metis_ncon_2); + EXPECT_NE(row4, nullptr); + EXPECT_NE(col4, nullptr); + //Graph2 has no edge weights + EXPECT_EQ(edgeWeights4, nullptr); + EXPECT_NE(vertexWeights4, nullptr); + + //Check vertex weights + for (int vertex = 0; vertex < n4; ++vertex) { + auto weights = vertexWeights4[vertex]->get_vals(); + for (int i = 0; i < metis_ncon_2; ++i){ + EXPECT_EQ(metis_vertex_weights_2[vertex + 1][i], weights[i]); //0 indexed + } + } + + //Check if all edges read correctly + std::set> edge_set4; + for (int i = 0; i < metis_m_2; ++i) { + edge_set4.emplace(metis_row_2[i] - 1, metis_col_2[i] - 1); //0 indexed + } + for (int i = 0; i < m4; i++) { + std::pair p(row4[i], col4[i]); + EXPECT_NE(edge_set4.find(p), edge_set4.end()); + } + + //File 1 with ValueType void/no weights + sparsebase::io::MetisGraphReader reader5("vertex_edge_weights.graph", false); + sparsebase::object::Graph* graph5 = reader5.ReadGraph(); + sparsebase::format::Format *con5 = graph5->get_connectivity(); + int n5 = con5->get_dimensions()[0]; + int m5 = con5->get_num_nnz(); + auto coo5 = con5->AsAbsolute>(); + auto row5 = coo5->get_row(); + auto col5 = coo5->get_col(); + //Check the dimensions + EXPECT_EQ(n5, metis_n_1 + 1); //1 indexed + EXPECT_EQ(m5, metis_m_1); + EXPECT_EQ(graph5->ncon_, 0); + EXPECT_NE(row5, nullptr); + EXPECT_NE(col5, nullptr); + EXPECT_EQ(coo5->get_vals(), nullptr); + EXPECT_EQ(graph5->vertexWeights_, nullptr); + + //Check if all edges read correctly + std::set> edge_set5; + for (int i = 0; i < metis_m_1; ++i) { + edge_set5.emplace(metis_row_1[i], metis_col_1[i]); + } + for (int i = 0; i < m5; i++) { + std::pair p(row5[i], col5[i]); + EXPECT_NE(edge_set5.find(p), edge_set5.end()); + } +} diff --git a/tests/suites/sparsebase/io/metis_graph_writer_tests.cc b/tests/suites/sparsebase/io/metis_graph_writer_tests.cc new file mode 100644 index 00000000..8942e6af --- /dev/null +++ b/tests/suites/sparsebase/io/metis_graph_writer_tests.cc @@ -0,0 +1,213 @@ +#include +#include +#include "gtest/gtest.h" +#include "reader_data.inc" +#include "sparsebase/sparsebase.h" +#include "sparsebase/object/object.h" +#include "sparsebase/io/metis_graph_reader.h" +#include "sparsebase/io/metis_graph_writer.h" +TEST(MetisGraphWriter, WriteGraph) { + //Write the metis graph data with vertex & edge weights to a file + std::ofstream ofs("vertex_edge_weights.graph"); + ofs << metis_graph_1; + ofs.close(); + + //Write the metis graph data with multiple vertex weights to a file + std::ofstream ofs2("multiple_vertex_weights.graph"); + ofs2 << metis_graph_2; + ofs2.close(); + + //File 1 + //Read the original graph 1 from data + sparsebase::io::MetisGraphReader org_reader1("vertex_edge_weights.graph", false); + sparsebase::object::Graph* org_graph1 = org_reader1.ReadGraph(); + sparsebase::format::Format *org_con1 = org_graph1->get_connectivity(); + int org_n1 = org_con1->get_dimensions()[0]; + int org_m1 = org_con1->get_num_nnz(); + int org_ncon1 = org_graph1->ncon_; + auto org_coo1 = org_con1->AsAbsolute>(); + auto org_row1 = org_coo1->get_row(); + auto org_col1 = org_coo1->get_col(); + auto org_val1 = org_coo1->get_vals(); + auto org_vertexWeights1 = org_graph1->vertexWeights_; + + //Write the original graph 1 + sparsebase::io::MetisGraphWriter writer1("org1.graph", + true,true,false); + writer1.WriteGraph(org_graph1); + //Read the output of writer + sparsebase::io::MetisGraphReader written_reader1("org1.graph", false); + sparsebase::object::Graph* written_graph1 = written_reader1.ReadGraph(); + sparsebase::format::Format *written_con1 = written_graph1->get_connectivity(); + int written_n1 = written_con1->get_dimensions()[0]; + int written_m1 = written_con1->get_num_nnz(); + int written_ncon1 = written_graph1->ncon_; + auto written_coo1 = written_con1->AsAbsolute>(); + auto written_row1 = written_coo1->get_row(); + auto written_col1 = written_coo1->get_col(); + auto written_val1 = written_coo1->get_vals(); + auto written_vertexWeights1 = written_graph1->vertexWeights_; + + //Compare the original graph 1 with the written version + //Check the dimensions + EXPECT_EQ(org_n1, written_n1); + EXPECT_EQ(org_m1, written_m1); + EXPECT_EQ(org_ncon1, written_ncon1); + //Check edges and weights + for (int i = 0; i < org_coo1->get_num_nnz(); ++i) { + EXPECT_EQ(org_row1[i], written_row1[i]); + EXPECT_EQ(org_col1[i], written_col1[i]); + EXPECT_EQ(org_val1[i], written_val1[i]); + } + //Check vertex weights + for (int vertex = 0; vertex < org_n1; ++vertex) { + auto org_weights = org_vertexWeights1[vertex]->get_vals(); + auto written_weights = written_vertexWeights1[vertex]->get_vals(); + for (int j = 0 ;j < org_ncon1; ++j) { + EXPECT_EQ(org_weights[j], written_weights[j]); + } + } + + //File 2 + //Read the original graph 2 from data + sparsebase::io::MetisGraphReader org_reader2("multiple_vertex_weights.graph", false); + sparsebase::object::Graph* org_graph2 = org_reader2.ReadGraph(); + sparsebase::format::Format *org_con2 = org_graph2->get_connectivity(); + int org_n2 = org_con2->get_dimensions()[0]; + int org_m2 = org_con2->get_num_nnz(); + int org_ncon2 = org_graph2->ncon_; + auto org_coo2 = org_con2->AsAbsolute>(); + auto org_row2= org_coo2->get_row(); + auto org_col2 = org_coo2->get_col(); + EXPECT_EQ(org_coo2->get_vals(), nullptr); + auto org_vertexWeights2 = org_graph2->vertexWeights_; + + //Write the original graph 2 + sparsebase::io::MetisGraphWriter writer2("org2.graph", + false,true,false); + writer2.WriteGraph(org_graph2); + //Read the output of writer + sparsebase::io::MetisGraphReader written_reader2("org2.graph", false); + sparsebase::object::Graph* written_graph2 = written_reader2.ReadGraph(); + sparsebase::format::Format *written_con2 = written_graph2->get_connectivity(); + int written_n2 = written_con2->get_dimensions()[0]; + int written_m2 = written_con2->get_num_nnz(); + int written_ncon2 = written_graph2->ncon_; + auto written_coo2 = written_con2->AsAbsolute>(); + auto written_row2 = written_coo2->get_row(); + auto written_col2 = written_coo2->get_col(); + auto written_vertexWeights2 = written_graph2->vertexWeights_; + + //Compare the original graph 2 with the written version + //Check the dimensions + EXPECT_EQ(org_n2, written_n2); + EXPECT_EQ(org_m2, written_m2); + EXPECT_EQ(org_ncon2, written_ncon2); + //Check edges + for (int i = 0; i < org_coo2->get_num_nnz(); ++i) { + EXPECT_EQ(org_row2[i], written_row2[i]); + EXPECT_EQ(org_col2[i], written_col2[i]); + } + //Check vertex weights + for (int vertex = 0; vertex < org_n2; ++vertex) { + auto org_weights = org_vertexWeights2[vertex]->get_vals(); + auto written_weights = written_vertexWeights2[vertex]->get_vals(); + for (int j = 0 ;j < org_ncon2; ++j) { + EXPECT_EQ(org_weights[j], written_weights[j]); + } + } + + //File 1, write from 0 based indices + //Read the original graph 3 from data + sparsebase::io::MetisGraphReader org_reader3("vertex_edge_weights.graph", true); + sparsebase::object::Graph* org_graph3 = org_reader3.ReadGraph(); + sparsebase::format::Format *org_con3 = org_graph3->get_connectivity(); + int org_n3 = org_con3->get_dimensions()[0]; + int org_m3 = org_con3->get_num_nnz(); + int org_ncon3 = org_graph3->ncon_; + auto org_coo3 = org_con3->AsAbsolute>(); + auto org_row3 = org_coo3->get_row(); + auto org_col3 = org_coo3->get_col(); + auto org_val3 = org_coo3->get_vals(); + auto org_vertexWeights3 = org_graph3->vertexWeights_; + + //Write the original graph 3 + sparsebase::io::MetisGraphWriter writer3("org3.graph", + true,true,true); + writer3.WriteGraph(org_graph3); + //Read the output of writer + sparsebase::io::MetisGraphReader written_reader3("org3.graph", true); + sparsebase::object::Graph* written_graph3 = written_reader3.ReadGraph(); + sparsebase::format::Format *written_con3 = written_graph3->get_connectivity(); + int written_n3 = written_con3->get_dimensions()[0]; + int written_m3 = written_con3->get_num_nnz(); + int written_ncon3 = written_graph3->ncon_; + auto written_coo3 = written_con3->AsAbsolute>(); + auto written_row3 = written_coo3->get_row(); + auto written_col3 = written_coo3->get_col(); + auto written_val3 = written_coo3->get_vals(); + auto written_vertexWeights3 = written_graph3->vertexWeights_; + + //Compare the original graph 3 with the written version + //Check the dimensions + EXPECT_EQ(org_n3, written_n3); + EXPECT_EQ(org_m3, written_m3); + EXPECT_EQ(org_ncon3, written_ncon3); + //Check edges and weights + for (int i = 0; i < org_coo3->get_num_nnz(); ++i) { + EXPECT_EQ(org_row3[i], written_row3[i]); + EXPECT_EQ(org_col3[i], written_col3[i]); + EXPECT_EQ(org_val3[i], written_val3[i]); + } + //Check vertex weights + for (int vertex = 0; vertex < org_n3; ++vertex) { + auto org_weights = org_vertexWeights3[vertex]->get_vals(); + auto written_weights = written_vertexWeights3[vertex]->get_vals(); + for (int j = 0 ;j < org_ncon3; ++j) { + EXPECT_EQ(org_weights[j], written_weights[j]); + } + } + + //File 1 with ValueType void/no weights + //Read the original graph 4 from data + sparsebase::io::MetisGraphReader org_reader4("vertex_edge_weights.graph", false); + sparsebase::object::Graph* org_graph4 = org_reader4.ReadGraph(); + sparsebase::format::Format *org_con4 = org_graph4->get_connectivity(); + int org_n4 = org_con4->get_dimensions()[0]; + int org_m4 = org_con4->get_num_nnz(); + EXPECT_EQ(org_graph4->ncon_, 0); + auto org_coo4 = org_con4->AsAbsolute>(); + auto org_row4 = org_coo4->get_row(); + auto org_col4 = org_coo4->get_col(); + EXPECT_EQ(org_coo4->get_vals(), nullptr); + EXPECT_EQ(org_graph4->vertexWeights_, nullptr); + + //Write the original graph 4 + sparsebase::io::MetisGraphWriter writer4("org4.graph", + false,false,false); + writer4.WriteGraph(org_graph4); + //Read the output of writer + sparsebase::io::MetisGraphReader written_reader4("org4.graph", false); + sparsebase::object::Graph* written_graph4 = written_reader4.ReadGraph(); + sparsebase::format::Format *written_con4 = written_graph4->get_connectivity(); + int written_n4 = written_con4->get_dimensions()[0]; + int written_m4 = written_con4->get_num_nnz(); + EXPECT_EQ(written_graph4->ncon_,0); + auto written_coo4 = written_con4->AsAbsolute>(); + auto written_row4 = written_coo4->get_row(); + auto written_col4 = written_coo4->get_col(); + EXPECT_EQ(written_coo4->get_vals(), nullptr); + EXPECT_EQ(written_graph4->vertexWeights_, nullptr); + + //Compare the original graph 4 with the written version + //Check the dimensions + EXPECT_EQ(org_n4, written_n4); + EXPECT_EQ(org_m4, written_m4); + + //Check edges + for (int i = 0; i < org_coo4->get_num_nnz(); ++i) { + EXPECT_EQ(org_row4[i], written_row4[i]); + EXPECT_EQ(org_col4[i], written_col4[i]); + } + +} diff --git a/tests/suites/sparsebase/io/mtx_reader_tests.cc b/tests/suites/sparsebase/io/mtx_reader_tests.cc index 7017c9ca..9348a96b 100644 --- a/tests/suites/sparsebase/io/mtx_reader_tests.cc +++ b/tests/suites/sparsebase/io/mtx_reader_tests.cc @@ -119,7 +119,7 @@ TEST(MTXReader, BasicsArray) { ofs2.close(); // Read non-weighted file using sparsebase - sparsebase::io::MTXReader reader("test.mtx"); + sparsebase::io::MTXReader reader("test_arr.mtx"); auto coo = reader.ReadCOO(); // Check the dimensions @@ -138,7 +138,7 @@ TEST(MTXReader, BasicsArray) { } // Read the weighted file using sparsebase - sparsebase::io::MTXReader reader2("test_values.mtx", true); + sparsebase::io::MTXReader reader2("test_arr_values.mtx", true); auto coo2 = reader2.ReadCOO(); // Check the dimensions @@ -211,3 +211,56 @@ TEST(MTXReader, BasicsSymmetric) { EXPECT_EQ(coo2->get_vals()[i], vals_symm[i]); } } + +TEST(MTXReader, BasicsSkewSymmetric) { + // Write the mtx data to a file + std::ofstream ofs("test_skew_symm.mtx"); + ofs << mtx_skew_symm_data; + ofs.close(); + + // Write the mtx data with values to a file + std::ofstream ofs2("test_skew_symm_values.mtx"); + ofs2 << mtx_skew_symm_data_with_values; + ofs2.close(); + + // Read non-weighted file using sparsebase + sparsebase::io::MTXReader reader("test_skew_symm.mtx"); + auto coo = reader.ReadCOO(); + + // Check the dimensions + EXPECT_EQ(coo->get_dimensions()[0], 5); + EXPECT_EQ(coo->get_dimensions()[1], 5); + EXPECT_EQ(coo->get_num_nnz(), 10); + + // Check that the arrays are populated + EXPECT_NE(coo->get_row(), nullptr); + EXPECT_NE(coo->get_col(), nullptr); + + // Check the integrity and order of data + for (int i = 0; i < 10; i++) { + EXPECT_EQ(coo->get_row()[i], row_skew_symm[i]); + EXPECT_EQ(coo->get_col()[i], col_skew_symm[i]); + } + + // Read the weighted file using sparsebase + sparsebase::io::MTXReader reader2("test_skew_symm_values.mtx", + true); + auto coo2 = reader2.ReadCOO(); + + // Check the dimensions + EXPECT_EQ(coo2->get_dimensions()[0], 5); + EXPECT_EQ(coo2->get_dimensions()[1], 5); + EXPECT_EQ(coo2->get_num_nnz(), 10); + + // vals array should not be empty or null (same for the other arrays) + EXPECT_NE(coo2->get_vals(), nullptr); + EXPECT_NE(coo2->get_row(), nullptr); + EXPECT_NE(coo2->get_col(), nullptr); + + // Check the integrity and order of data + for (int i = 0; i < 10; i++) { + EXPECT_EQ(coo2->get_row()[i], row_skew_symm[i]); + EXPECT_EQ(coo2->get_col()[i], col_skew_symm[i]); + EXPECT_EQ(coo2->get_vals()[i], vals_skew_symm[i]); + } +} diff --git a/tests/suites/sparsebase/io/mtx_writer_tests.cc b/tests/suites/sparsebase/io/mtx_writer_tests.cc new file mode 100644 index 00000000..ef44b852 --- /dev/null +++ b/tests/suites/sparsebase/io/mtx_writer_tests.cc @@ -0,0 +1,250 @@ +#include +#include + +#include "gtest/gtest.h" +//#include "reader_data.inc" +#include "sparsebase/sparsebase.h" +#include "sparsebase/io/mtx_writer.h" + +TEST(MTXWriter, WriteCOO) { + // Initialize a COO for testing + int row_1[4]{0, 1, 2, 3}; + int col_1[4]{0, 2, 1, 3}; + float vals_1[4]{0.1, 0.2, 0.3, 0.4}; + sparsebase::format::COO coo_1(4, 4, 4, row_1, col_1, vals_1, + sparsebase::format::kNotOwned); + + // Write the COO to a Mtx file with sparsebase + sparsebase::io::MTXWriter writerCOO_1("writer_test_coo_mtx1.mtx"); + writerCOO_1.WriteCOO(&coo_1); + + // Read the COO from the Mtx file with sparsebase + sparsebase::io::MTXReader readerCOO_1("writer_test_coo_mtx1.mtx"); + + auto coo_1_r = readerCOO_1.ReadCOO(); + + // Compare the dimensions + EXPECT_EQ(coo_1.get_dimensions(), coo_1_r->get_dimensions()); + EXPECT_EQ(coo_1.get_num_nnz(), coo_1_r->get_num_nnz()); + + // Compare the underlying arrays + for (int i = 0; i < 4; i++) { + EXPECT_EQ(coo_1.get_row()[i], coo_1_r->get_row()[i]); + EXPECT_EQ(coo_1.get_col()[i], coo_1_r->get_col()[i]); + EXPECT_EQ(coo_1.get_vals()[i], coo_1_r->get_vals()[i]); + } +} + +TEST(MTXWriter, WriteCOO_falseSymmetric) { + // Initialize a COO for testing + int row_2[4]{0, 1, 3, 3}; + int col_2[4]{0, 2, 1, 3}; + float vals_2[4]{0.1, 0.2, 0.3, 0.4}; + sparsebase::format::COO coo_2(4, 4, 4, row_2, col_2, vals_2, + sparsebase::format::kNotOwned); + + // Write the COO to a Mtx file with sparsebase + sparsebase::io::MTXWriter writerCOO_2("writer_test_coo_mtx2.mtx", "matrix", "coordinate", "real", "symmetric"); + EXPECT_THROW( + (writerCOO_2.WriteCOO(&coo_2)), + sparsebase::utils::WriterException); +} + +TEST(MTXWriter, WriteCOO_trueSymmetric) { + // Initialize a COO for testing + int row_3[3]{0, 1, 2}; + int col_3[3]{0, 2, 1}; + float vals_3[3]{0.1, 0.3, 0.3}; + sparsebase::format::COO coo_3(4, 4, 3, row_3, col_3, vals_3, + sparsebase::format::kNotOwned); + + // Write the COO to a Mtx file with sparsebase + sparsebase::io::MTXWriter writerCOO_3("writer_test_coo_mtx3.mtx", "matrix", "coordinate", "real", "symmetric"); + writerCOO_3.WriteCOO(&coo_3); + + // Read the COO from the Mtx file with sparsebase + sparsebase::io::MTXReader readerCOO_3("writer_test_coo_mtx3.mtx"); + + auto coo_3_r = readerCOO_3.ReadCOO(); + + // Compare the dimensions + EXPECT_EQ(coo_3.get_dimensions(), coo_3_r->get_dimensions()); + EXPECT_EQ(coo_3.get_num_nnz(), coo_3_r->get_num_nnz()); + + // Compare the underlying arrays + for (int i = 0; i < 3; i++) { + EXPECT_EQ(coo_3.get_row()[i], coo_3_r->get_row()[i]); + EXPECT_EQ(coo_3.get_col()[i], coo_3_r->get_col()[i]); + EXPECT_EQ(coo_3.get_vals()[i], coo_3_r->get_vals()[i]); + } +} + +TEST(MTXWriter, WriteCOO_array) { + // Initialize a COO for testing + int row_4[5]{1, 2, 3, 4, 4}; + int col_4[5]{0, 1, 0, 2, 3}; + float vals_4[5]{0.1, 0.3, 0.2, 0.4, 0.5}; + + sparsebase::format::COO coo_4(5, 5, 5, row_4, col_4, vals_4, + sparsebase::format::kNotOwned); + + // Write the COO to a Mtx file with sparsebase + sparsebase::io::MTXWriter writerCOO_4("writer_test_coo_mtx4.mtx", "matrix", "array", "real", "general"); + writerCOO_4.WriteCOO(&coo_4); + + // Read the COO from the Mtx file with sparsebase + sparsebase::io::MTXReader readerCOO_4("writer_test_coo_mtx4.mtx"); + + auto coo_4_r = readerCOO_4.ReadCOO(); + + // Compare the dimensions + EXPECT_EQ(coo_4.get_dimensions(), coo_4_r->get_dimensions()); + EXPECT_EQ(coo_4.get_num_nnz(), coo_4_r->get_num_nnz()); + + // Compare the underlying arrays + for (int i = 0; i < 5; i++) { + EXPECT_EQ(coo_4.get_row()[i], coo_4_r->get_row()[i]); + EXPECT_EQ(coo_4.get_col()[i], coo_4_r->get_col()[i]); + EXPECT_EQ(coo_4.get_vals()[i], coo_4_r->get_vals()[i]); + } +} + +TEST(MTXWriter, WriteCOO_SkewSymmetric) { + // Initialize a COO for testing + int row_5[2]{1, 2}; + int col_5[2]{2, 1}; + int vals_5[2]{-3, 3}; + sparsebase::format::COO coo_5(4, 4, 2, row_5, col_5, vals_5, + sparsebase::format::kNotOwned); + + // Write the COO to a Mtx file with sparsebase + sparsebase::io::MTXWriter writerCOO_5("writer_test_coo_mtx5.mtx", "matrix", "coordinate", "integer", "skew-symmetric"); + writerCOO_5.WriteCOO(&coo_5); + + // Read the COO from the Mtx file with sparsebase + sparsebase::io::MTXReader readerCOO_5("writer_test_coo_mtx5.mtx"); + + auto coo_5_r = readerCOO_5.ReadCOO(); + + // Compare the dimensions + EXPECT_EQ(coo_5.get_dimensions(), coo_5_r->get_dimensions()); + EXPECT_EQ(coo_5.get_num_nnz(), coo_5_r->get_num_nnz()); + + // Compare the underlying arrays + for (int i = 0; i < 2; i++) { + EXPECT_EQ(coo_5.get_row()[i], coo_5_r->get_row()[i]); + EXPECT_EQ(coo_5.get_col()[i], coo_5_r->get_col()[i]); + EXPECT_EQ(coo_5.get_vals()[i], coo_5_r->get_vals()[i]); + } +} + +TEST(MTXWriter, WriteCOO_PatternSymmetric) { + // Initialize a COO for testing + int row_6[11]{0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4}; + int col_6[11]{0, 1, 3, 0, 2, 1, 4, 0, 4, 2, 3}; + float vals_6[11]{0.7, 0.1, 0.2, 0.1, 0.3, 0.3, 0.4, 0.2, 0.5, 0.4, 0.5}; + sparsebase::format::COO coo_6(5, 5, 11, row_6, col_6, vals_6, + sparsebase::format::kNotOwned); + + // Write the COO to a Mtx file with sparsebase + sparsebase::io::MTXWriter writerCOO_6("writer_test_coo_mtx6.mtx", "matrix", "coordinate", "pattern", "symmetric"); + writerCOO_6.WriteCOO(&coo_6); + + // Read the COO from the Mtx file with sparsebase + sparsebase::io::MTXReader readerCOO_6("writer_test_coo_mtx6.mtx"); + + auto coo_6_r = readerCOO_6.ReadCOO(); + + // Compare the dimensions + EXPECT_EQ(coo_6.get_dimensions(), coo_6_r->get_dimensions()); + EXPECT_EQ(coo_6.get_num_nnz(), coo_6_r->get_num_nnz()); + + // Compare the underlying arrays + for (int i = 0; i < 11; i++) { + EXPECT_EQ(coo_6.get_row()[i], coo_6_r->get_row()[i]); + EXPECT_EQ(coo_6.get_col()[i], coo_6_r->get_col()[i]); + } +} + +TEST(MTXWriter, WriteCSR) { + // Initialize a CSR for testing + int row_10[6]{0, 0, 1, 2, 3, 5}; + int col_10[5]{0, 1, 0, 2, 3}; + float vals_10[5]{0.1, 0.3, 0.2, 0.4, 0.5}; + + sparsebase::format::CSR csr_1(5, 5, row_10, col_10, vals_10, + sparsebase::format::kNotOwned); + + // Write the CSR to a Mtx file with sparsebase + sparsebase::io::MTXWriter writerCSR_1("writer_test_csr_mtx.mtx"); + writerCSR_1.WriteCSR(&csr_1); + + // Read the CSR from the Mtx file with sparsebase + sparsebase::io::MTXReader readerCSR_1("writer_test_csr_mtx.mtx"); + + auto csr_1_r = readerCSR_1.ReadCSR(); + + // Compare the dimensions + EXPECT_EQ(csr_1.get_dimensions(), csr_1_r->get_dimensions()); + EXPECT_EQ(csr_1.get_num_nnz(), csr_1_r->get_num_nnz()); + + // Compare the underlying arrays + for (int i = 0; i < 5; i++) { + EXPECT_EQ(csr_1.get_col()[i], csr_1_r->get_col()[i]); + EXPECT_EQ(csr_1.get_vals()[i], csr_1_r->get_vals()[i]); + } + for (int i = 0; i < 6; i++) { + EXPECT_EQ(csr_1.get_row_ptr()[i], csr_1_r->get_row_ptr()[i]); + } +} + +TEST(MTXWriter, WriteArray) { + // Initialize an array for testing + float array[5]{0.1, 0.2, 0.3, 0.4, 0.5}; + sparsebase::format::Array sbArray_1(5, array, + sparsebase::format::kNotOwned); + + // Write the CSR to a Mtx file with sparsebase + sparsebase::io::MTXWriter writerArray_1("writer_test_array_mtx.mtx", "matrix", "array", "real", "general"); + writerArray_1.WriteArray(&sbArray_1); + + // Read the CSR from the Mtx file with sparsebase + sparsebase::io::MTXReader readerArray_1("writer_test_array_mtx.mtx"); + auto sbArray_1_r = readerArray_1.ReadArray(); + + // Compare the arrays + for (int i = 0; i < 5; i++) { + EXPECT_EQ(array[i], sbArray_1_r->get_vals()[i]); + } +} + +TEST(MTXWriter, WriteCOO_falseSymmetricRecntangle) { + // Initialize a COO for testing + int row_7[3]{0, 1, 2}; + int col_7[3]{0, 2, 1}; + float vals_7[3]{0.1, 0.3, 0.3}; + sparsebase::format::COO coo_7(4, 5, 3, row_7, col_7, vals_7, + sparsebase::format::kNotOwned); + + // Write the COO to a Mtx file with sparsebase + sparsebase::io::MTXWriter writerCOO_7("writer_test_coo_mtx7.mtx", "matrix", "coordinate", "real", "symmetric"); + + EXPECT_THROW( + (writerCOO_7.WriteCOO(&coo_7)), + sparsebase::utils::WriterException); +} + +TEST(MTXWriter, WriteCOO_voidValueNonPattern) { + // Initialize a COO for testing + int row_8[3]{0, 1, 2}; + int col_8[3]{0, 2, 1}; + sparsebase::format::COO coo_8(4, 4, 3, row_8, col_8, nullptr, + sparsebase::format::kNotOwned); + + // Write the COO to a Mtx file with sparsebase + sparsebase::io::MTXWriter writerCOO_8("writer_test_coo_mtx8.mtx", "matrix", "coordinate", "real", "symmetric"); + + EXPECT_THROW( + (writerCOO_8.WriteCOO(&coo_8)), + sparsebase::utils::WriterException); +} \ No newline at end of file diff --git a/tests/suites/sparsebase/io/patoh_reader_tests.cc b/tests/suites/sparsebase/io/patoh_reader_tests.cc new file mode 100644 index 00000000..34ec9b60 --- /dev/null +++ b/tests/suites/sparsebase/io/patoh_reader_tests.cc @@ -0,0 +1,257 @@ +#include +#include "gtest/gtest.h" +#include "reader_data.inc" +#include "sparsebase/sparsebase.h" +#include "sparsebase/object/object.h" +#include "sparsebase/io/patoh_reader.h" + +TEST(PatohReader,ReadHyperGraph1){ + //Write the hypergraph data with no edge and vertices weight to a file + std::ofstream ofs("HyperGraph_no_Edge_and_Vertices_Weight.hypeg"); + ofs << hypergraph_1; + ofs.close(); + + sparsebase::io::PatohReader ReadHyperGraph1("HyperGraph_no_Edge_and_Vertices_Weight.hypeg"); + sparsebase::object::HyperGraph* hypergraph1 = ReadHyperGraph1.ReadHyperGraph(); + sparsebase::format::Format *con = hypergraph1->get_connectivity(); + int n_ = hypergraph1->n_; + int m_ = hypergraph1->m_; + int constraint_num_ = hypergraph1->constraint_num_; + int base_type_ = hypergraph1->base_type_; + int vertex_size = con->get_dimensions()[1]; + auto xpins = con->AsAbsolute>()->get_row_ptr(); + auto pins = con->AsAbsolute>()->get_col(); + auto xpin_val_arr = con->AsAbsolute>()->get_vals(); + auto xNetsCSR = hypergraph1->xNetCSR_; + auto xnets = xNetsCSR->get_row_ptr(); + auto cells = xNetsCSR->get_col(); + auto xnet_val_arr = xNetsCSR->get_vals(); + auto netWeights = hypergraph1->netWeights_; + auto cellWeights = hypergraph1->cellWeights_; + + //Check the dimensions + EXPECT_EQ(n_,hypergraph1_n); + EXPECT_EQ(vertex_size, hypergraph1_vertex_size); + EXPECT_EQ(m_,hypergraph1_m); + EXPECT_EQ(constraint_num_,hypergraph1_constraint_num); + EXPECT_EQ(base_type_,hypergraph1_base_type); + EXPECT_NE(xpins, nullptr); + EXPECT_NE(pins, nullptr); + EXPECT_NE(xnets, nullptr); + EXPECT_NE(cells, nullptr); + EXPECT_NE(netWeights, nullptr); + EXPECT_NE(cellWeights, nullptr); + EXPECT_EQ(xpin_val_arr, nullptr); + EXPECT_EQ(xnet_val_arr, nullptr); + + //Check xpins + for (int i = 0; i < n_+1; ++i) + EXPECT_EQ(hypergraph1_xpins[i], xpins[i]); + + //Check pins + for (int i = 0; i < m_; ++i) + EXPECT_EQ(hypergraph1_pins[i], pins[i]); + + //Check edge weights + auto netWeightsVal = netWeights->get_vals(); + for (int i = 0; i < n_; ++i) + EXPECT_EQ(hypergraph1_netWeights[i], netWeightsVal[i]); + + //Check cell weights + auto cellWeightsVal = cellWeights->get_vals(); + for (int i = 0; i < vertex_size; ++i) + EXPECT_EQ(hypergraph1_cellWeights[i], cellWeightsVal[i]); + + // Check xnets + for (int i = 0; i ReadHyperGraph2("HyperGraph_no_Vertices_Weight.hypeg"); + sparsebase::object::HyperGraph* hypergraph2 = ReadHyperGraph2.ReadHyperGraph(); + sparsebase::format::Format *con2 = hypergraph2->get_connectivity(); + int n2_ = hypergraph2->n_; + int m2_ = hypergraph2->m_; + int vertex_size2 = con2->get_dimensions()[1]; + auto xpins2 = con2->AsAbsolute>()->get_row_ptr(); + auto pins2 = con2->AsAbsolute>()->get_col(); + auto xNetsCSR2 = hypergraph2->xNetCSR_; + auto xnets2 = xNetsCSR2->get_row_ptr(); + auto cells2 = xNetsCSR2->get_col(); + auto xnet_val_arr2 = xNetsCSR2->get_vals(); + auto xpin_val_arr2 = con2->AsAbsolute>()->get_vals(); + auto netWeights2 = hypergraph2->netWeights_; + auto cellWeights2 = hypergraph2->cellWeights_; + + //Check the dimensions + EXPECT_EQ(n2_,hypergraph2_n); + EXPECT_EQ(vertex_size2, hypergraph2_vertex_size); + EXPECT_EQ(m2_,hypergraph2_m); + EXPECT_NE(xpins2, nullptr); + EXPECT_NE(pins2, nullptr); + EXPECT_NE(xnets2, nullptr); + EXPECT_NE(cells2, nullptr); + EXPECT_NE(netWeights2, nullptr); + EXPECT_NE(cellWeights2, nullptr); + EXPECT_EQ(xpin_val_arr2, nullptr); + EXPECT_EQ(xnet_val_arr2, nullptr); + + //Check xpins + for (int i = 0; i < n2_+1; ++i) + EXPECT_EQ(hypergraph2_xpins[i], xpins2[i]); + + //Check pins + for (int i = 0; i < m2_; ++i) + EXPECT_EQ(hypergraph2_pins[i], pins2[i]); + + //Check edge weights + auto netWeightsVal2 = netWeights2->get_vals(); + for (int i = 0; i < n2_; ++i) + EXPECT_EQ(hypergraph2_netWeights[i], netWeightsVal2[i]); + + //Check cell weights + auto cellWeightsVal2 = cellWeights2->get_vals(); + for (int i = 0; i < vertex_size2; ++i) + EXPECT_EQ(hypergraph2_cellWeights[i], cellWeightsVal2[i]); + + // Check xnets + for (int i = 0; i<(vertex_size2)+1;++i) + EXPECT_EQ(hypergraph2_xnets[i], xnets2[i]); + + // Check cells + for (int i = 0; i ReadHyperGraph3("HyperGraph_no_Nets_Weight.hypeg"); + sparsebase::object::HyperGraph* hypergraph3 = ReadHyperGraph3.ReadHyperGraph(); + sparsebase::format::Format *con3 = hypergraph3->get_connectivity(); + int n3_ = hypergraph3->n_; + int m3_ = hypergraph3->m_; + int vertex_size3 = con3->get_dimensions()[1]; + auto xpins3 = con3->AsAbsolute>()->get_row_ptr(); + auto pins3 = con3->AsAbsolute>()->get_col(); + auto xNetsCSR3 = hypergraph3->xNetCSR_; + auto xnets3 = xNetsCSR3->get_row_ptr(); + auto cells3 = xNetsCSR3->get_col(); + auto xnet_val_arr3 = xNetsCSR3->get_vals(); + auto xpin_val_arr3 = con3->AsAbsolute>()->get_vals(); + auto netWeights3 = hypergraph3->netWeights_; + auto cellWeights3 = hypergraph3->cellWeights_; + + //Check the dimensions + EXPECT_EQ(n3_,hypergraph3_n); + EXPECT_EQ(vertex_size3, hypergraph3_vertex_size); + EXPECT_EQ(m3_,hypergraph3_m); + EXPECT_NE(xpins3, nullptr); + EXPECT_NE(pins3, nullptr); + EXPECT_NE(xnets3, nullptr); + EXPECT_NE(cells3, nullptr); + EXPECT_NE(netWeights3, nullptr); + EXPECT_NE(cellWeights3, nullptr); + EXPECT_EQ(xpin_val_arr3, nullptr); + EXPECT_EQ(xnet_val_arr3, nullptr); + + //Check xpins + for (int i = 0; i < n3_+1; ++i) + EXPECT_EQ(hypergraph3_xpins[i], xpins3[i]); + + //Check pins + for (int i = 0; i < m3_; ++i) + EXPECT_EQ(hypergraph3_pins[i], pins3[i]); + + //Check edge weights + auto netWeightsVal3 = netWeights3->get_vals(); + for (int i = 0; i < n3_; ++i) + EXPECT_EQ(hypergraph3_netWeights[i], netWeightsVal3[i]); + + //Check cell weights + auto cellWeightsVal3 = cellWeights3->get_vals(); + for (int i = 0; i < vertex_size3; ++i) + EXPECT_EQ(hypergraph3_cellWeights[i], cellWeightsVal3[i]); + + // Check xnets + for (int i = 0; i<(vertex_size3)+1;++i) + EXPECT_EQ(hypergraph3_xnets[i], xnets3[i]); + + // Check cells + for (int i = 0; i ReadHyperGraph4("HyperGraph_With_Cell_and_Nets_Weight.hypeg"); + sparsebase::object::HyperGraph* hypergraph4 = ReadHyperGraph4.ReadHyperGraph(); + sparsebase::format::Format *con4 = hypergraph4->get_connectivity(); + int n4_ = hypergraph4->n_; + int m4_ = hypergraph4->m_; + int vertex_size4 = con4->get_dimensions()[1]; + auto xpins4 = con4->AsAbsolute>()->get_row_ptr(); + auto pins4 = con4->AsAbsolute>()->get_col(); + auto xNetsCSR4 = hypergraph4->xNetCSR_; + auto xnets4 = xNetsCSR4->get_row_ptr(); + auto cells4 = xNetsCSR4->get_col(); + auto xnet_val_arr4 = xNetsCSR4->get_vals(); + auto xpin_val_arr4 = con4->AsAbsolute>()->get_vals(); + auto netWeights4 = hypergraph4->netWeights_; + auto cellWeights4 = hypergraph4->cellWeights_; + + //Check the dimensions + EXPECT_EQ(n4_,hypergraph4_n); + EXPECT_EQ(vertex_size4, hypergraph4_vertex_size); + EXPECT_EQ(m4_,hypergraph4_m); + EXPECT_NE(xpins4, nullptr); + EXPECT_NE(pins4, nullptr); + EXPECT_NE(xnets4, nullptr); + EXPECT_NE(cells4, nullptr); + EXPECT_NE(netWeights4, nullptr); + EXPECT_NE(cellWeights4, nullptr); + EXPECT_EQ(xpin_val_arr4, nullptr); + EXPECT_EQ(xnet_val_arr4, nullptr); + + //Check xpins + for (int i = 0; i < n4_+1; ++i) + EXPECT_EQ(hypergraph4_xpins[i], xpins4[i]); + + //Check pins + for (int i = 0; i < m4_; ++i) + EXPECT_EQ(hypergraph4_pins[i], pins4[i]); + + //Check edge weights + auto netWeightsVal4 = netWeights4->get_vals(); + for (int i = 0; i < n4_; ++i) + EXPECT_EQ(hypergraph4_netWeights[i], netWeightsVal4[i]); + + //Check cell weights + auto cellWeightsVal4 = cellWeights4->get_vals(); + for (int i = 0; i < vertex_size4; ++i) + EXPECT_EQ(hypergraph4_cellWeights[i], cellWeightsVal4[i]); + + // Check xnets + for (int i = 0; i<(vertex_size4)+1;++i) + EXPECT_EQ(hypergraph4_xnets[i], xnets4[i]); + + // Check cells + for (int i = 0; i +#include "gtest/gtest.h" +#include "reader_data.inc" +#include "sparsebase/sparsebase.h" +#include "sparsebase/object/object.h" +#include "sparsebase/io/patoh_reader.h" +#include "sparsebase/io/patoh_writer.h" + +TEST(PatohWriter,WriteHyperGraph){ + //Write the hypergraph data with no edge and vertices weight to a file + std::ofstream ofs("HyperGraph_no_Edge_and_Vertices_Weight.hypeg"); + ofs << hypergraph_1; + ofs.close(); + + //Read the original hypergraph1 from data + sparsebase::io::PatohReader ReadHyperGraph1("HyperGraph_no_Edge_and_Vertices_Weight.hypeg"); + sparsebase::object::HyperGraph* org_hypergraph1 = ReadHyperGraph1.ReadHyperGraph(); + sparsebase::format::Format *org_con = org_hypergraph1->get_connectivity(); + int org_n1 = org_hypergraph1->n_; + int org_m1 = org_hypergraph1->m_; + int org_constraint_num1 = org_hypergraph1->constraint_num_; + int org_base_type1 = org_hypergraph1->base_type_; + int org_vertex_size1 = org_con->get_dimensions()[1]; + auto org_xpins1 = org_con->AsAbsolute>()->get_row_ptr(); + auto org_pins1 = org_con->AsAbsolute>()->get_col(); + auto org_xpin_val_arr1 = org_con->AsAbsolute>()->get_vals(); + auto org_xNetsCSR1 = org_hypergraph1->xNetCSR_; + auto org_xnets1 = org_xNetsCSR1->get_row_ptr(); + auto org_cells1 = org_xNetsCSR1->get_col(); + auto org_xnet_val_arr1 = org_xNetsCSR1->get_vals(); + auto org_netWeights1 = org_hypergraph1->netWeights_; + auto org_cellWeights1 = org_hypergraph1->cellWeights_; + + //Write the original graph1 + sparsebase::io::PatohWriter WriteHyperGraph1("org_hypergraph1.hypeg",true); + WriteHyperGraph1.WriteHyperGraph(org_hypergraph1); + + //Read the output of writer + sparsebase::io::PatohReader Read_Written_HyperGraph1("org_hypergraph1.hypeg"); + sparsebase::object::HyperGraph* written_hypergraph1 = Read_Written_HyperGraph1.ReadHyperGraph(); + sparsebase::format::Format *written_con = written_hypergraph1->get_connectivity(); + + int written_n1 = written_hypergraph1->n_; + int written_m1 = written_hypergraph1->m_; + int written_constraint_num1 = written_hypergraph1->constraint_num_; + int written_base_type1 = written_hypergraph1->base_type_; + int written_vertex_size1 = written_con->get_dimensions()[1]; + auto written_xpins1 = written_con->AsAbsolute>()->get_row_ptr(); + auto written_pins1 = written_con->AsAbsolute>()->get_col(); + auto written_xpin_val_arr1 = written_con->AsAbsolute>()->get_vals(); + auto written_xNetsCSR1 = written_hypergraph1->xNetCSR_; + auto written_xnets1 = written_xNetsCSR1->get_row_ptr(); + auto written_cells1 = written_xNetsCSR1->get_col(); + auto written_xnet_val_arr1 = written_xNetsCSR1->get_vals(); + auto written_netWeights1 = written_hypergraph1->netWeights_; + auto written_cellWeights1 = written_hypergraph1->cellWeights_; + + //Compare the original hypergraph 1 with the written version + //Check the dimensions + EXPECT_EQ(org_n1,written_n1); + EXPECT_EQ(org_vertex_size1, written_vertex_size1); + EXPECT_EQ(org_m1,written_m1); + EXPECT_EQ(org_constraint_num1,written_constraint_num1); + EXPECT_EQ(org_base_type1,written_base_type1); + + EXPECT_EQ(written_xpin_val_arr1, nullptr); + EXPECT_EQ(written_xnet_val_arr1, nullptr); + + //Check xpins + for (int i = 0; i < org_n1+1; ++i) + EXPECT_EQ(org_xpins1[i], written_xpins1[i]); + + //Check pins + for (int i = 0; i < org_m1; ++i) + EXPECT_EQ(org_pins1[i], written_pins1[i]); + + //Check edge weights + auto org_netWeightsVal1 = org_netWeights1->get_vals(); + auto written_netWeightsVal1 = written_netWeights1->get_vals(); + for (int i = 0; i < org_n1; ++i) + EXPECT_EQ(org_netWeightsVal1[i], written_netWeightsVal1[i]); + + //Check cell weights + auto org_cellWeightsVal1 = org_cellWeights1->get_vals(); + auto written_cellWeightsVal1 = written_cellWeights1->get_vals(); + + for (int i = 0; i < org_vertex_size1; ++i) + EXPECT_EQ(org_cellWeightsVal1[i], written_cellWeightsVal1[i]); + + // Check xnets + for (int i = 0; i ReadHyperGraph2("HyperGraph_no_Vertices_Weight.hypeg"); + sparsebase::object::HyperGraph* org_hypergraph2 = ReadHyperGraph2.ReadHyperGraph(); + sparsebase::format::Format *org_con2 = org_hypergraph2->get_connectivity(); + int org_n2 = org_hypergraph2->n_; + int org_m2 = org_hypergraph2->m_; + int org_constraint_num2 = org_hypergraph2->constraint_num_; + int org_base_type2 = org_hypergraph2->base_type_; + int org_vertex_size2 = org_con2->get_dimensions()[1]; + auto org_xpins2 = org_con2->AsAbsolute>()->get_row_ptr(); + auto org_pins2 = org_con2->AsAbsolute>()->get_col(); + auto org_xpin_val_arr2 = org_con2->AsAbsolute>()->get_vals(); + auto org_xNetsCSR2 = org_hypergraph2->xNetCSR_; + auto org_xnets2 = org_xNetsCSR2->get_row_ptr(); + auto org_cells2 = org_xNetsCSR2->get_col(); + auto org_xnet_val_arr2 = org_xNetsCSR2->get_vals(); + auto org_netWeights2 = org_hypergraph2->netWeights_; + auto org_cellWeights2 = org_hypergraph2->cellWeights_; + + //Write the original graph2 + sparsebase::io::PatohWriter WriteHyperGraph2("org_hypergraph2.hypeg",false,true,false); + WriteHyperGraph2.WriteHyperGraph(org_hypergraph2); + + //Read the output of writer + sparsebase::io::PatohReader Read_Written_HyperGraph2("org_hypergraph2.hypeg"); + sparsebase::object::HyperGraph* written_hypergraph2 = Read_Written_HyperGraph2.ReadHyperGraph(); + sparsebase::format::Format *written_con2 = written_hypergraph2->get_connectivity(); + + int written_n2 = written_hypergraph2->n_; + int written_m2 = written_hypergraph2->m_; + int written_constraint_num2 = written_hypergraph2->constraint_num_; + int written_base_type2 = written_hypergraph2->base_type_; + int written_vertex_size2 = written_con2->get_dimensions()[1]; + auto written_xpins2 = written_con2->AsAbsolute>()->get_row_ptr(); + auto written_pins2 = written_con2->AsAbsolute>()->get_col(); + auto written_xpin_val_arr2 = written_con2->AsAbsolute>()->get_vals(); + auto written_xNetsCSR2 = written_hypergraph2->xNetCSR_; + auto written_xnets2 = written_xNetsCSR2->get_row_ptr(); + auto written_cells2 = written_xNetsCSR2->get_col(); + auto written_xnet_val_arr2 = written_xNetsCSR2->get_vals(); + auto written_netWeights2 = written_hypergraph2->netWeights_; + auto written_cellWeights2 = written_hypergraph2->cellWeights_; + + //Compare the original hypergraph 2 with the written version + //Check the dimensions + EXPECT_EQ(org_n2,written_n2); + EXPECT_EQ(org_vertex_size2, written_vertex_size2); + EXPECT_EQ(org_m2,written_m2); + EXPECT_EQ(org_constraint_num2,written_constraint_num2); + EXPECT_EQ(org_base_type2,written_base_type2); + + EXPECT_EQ(written_xpin_val_arr2, nullptr); + EXPECT_EQ(written_xnet_val_arr2, nullptr); + + //Check xpins + for (int i = 0; i < org_n2+1; ++i) + EXPECT_EQ(org_xpins2[i], written_xpins2[i]); + + //Check pins + for (int i = 0; i < org_m2; ++i) + EXPECT_EQ(org_pins2[i], written_pins2[i]); + + //Check edge weights + auto org_netWeightsVal2 = org_netWeights2->get_vals(); + auto written_netWeightsVal2 = written_netWeights2->get_vals(); + for (int i = 0; i < org_n2; ++i) + EXPECT_EQ(org_netWeightsVal2[i], written_netWeightsVal2[i]); + + //Check cell weights + auto org_cellWeightsVal2 = org_cellWeights2->get_vals(); + auto written_cellWeightsVal2 = written_cellWeights2->get_vals(); + + for (int i = 0; i < org_vertex_size2; ++i) + EXPECT_EQ(org_cellWeightsVal2[i], written_cellWeightsVal2[i]); + + // Check xnets + for (int i = 0; i ReadHyperGraph3("HyperGraph_no_Nets_Weight.hypeg"); + sparsebase::object::HyperGraph* org_hypergraph3 = ReadHyperGraph3.ReadHyperGraph(); + sparsebase::format::Format *org_con3 = org_hypergraph3->get_connectivity(); + int org_n3 = org_hypergraph3->n_; + int org_m3 = org_hypergraph3->m_; + int org_constraint_num3 = org_hypergraph3->constraint_num_; + int org_base_type3 = org_hypergraph3->base_type_; + int org_vertex_size3 = org_con3->get_dimensions()[1]; + auto org_xpins3 = org_con3->AsAbsolute>()->get_row_ptr(); + auto org_pins3 = org_con3->AsAbsolute>()->get_col(); + auto org_xpin_val_arr3 = org_con3->AsAbsolute>()->get_vals(); + auto org_xNetsCSR3 = org_hypergraph3->xNetCSR_; + auto org_xnets3 = org_xNetsCSR3->get_row_ptr(); + auto org_cells3 = org_xNetsCSR3->get_col(); + auto org_xnet_val_arr3 = org_xNetsCSR3->get_vals(); + auto org_netWeights3 = org_hypergraph3->netWeights_; + auto org_cellWeights3 = org_hypergraph3->cellWeights_; + + //Write the original graph3 + sparsebase::io::PatohWriter WriteHyperGraph3("org_hypergraph3.hypeg",false,false,true); + WriteHyperGraph3.WriteHyperGraph(org_hypergraph3); + + //Read the output of writer + sparsebase::io::PatohReader Read_Written_HyperGraph3("org_hypergraph3.hypeg"); + sparsebase::object::HyperGraph* written_hypergraph3 = Read_Written_HyperGraph3.ReadHyperGraph(); + sparsebase::format::Format *written_con3 = written_hypergraph3->get_connectivity(); + + int written_n3 = written_hypergraph3->n_; + int written_m3 = written_hypergraph3->m_; + int written_constraint_num3 = written_hypergraph3->constraint_num_; + int written_base_type3 = written_hypergraph3->base_type_; + int written_vertex_size3 = written_con3->get_dimensions()[1]; + auto written_xpins3 = written_con3->AsAbsolute>()->get_row_ptr(); + auto written_pins3 = written_con3->AsAbsolute>()->get_col(); + auto written_xpin_val_arr3 = written_con3->AsAbsolute>()->get_vals(); + auto written_xNetsCSR3 = written_hypergraph3->xNetCSR_; + auto written_xnets3 = written_xNetsCSR3->get_row_ptr(); + auto written_cells3 = written_xNetsCSR3->get_col(); + auto written_xnet_val_arr3 = written_xNetsCSR3->get_vals(); + auto written_netWeights3 = written_hypergraph3->netWeights_; + auto written_cellWeights3 = written_hypergraph3->cellWeights_; + + //Compare the original hypergraph 3 with the written version + //Check the dimensions + EXPECT_EQ(org_n3,written_n3); + EXPECT_EQ(org_vertex_size3, written_vertex_size3); + EXPECT_EQ(org_m3,written_m3); + EXPECT_EQ(org_constraint_num3,written_constraint_num3); + EXPECT_EQ(org_base_type3,written_base_type3); + + EXPECT_EQ(written_xpin_val_arr3, nullptr); + EXPECT_EQ(written_xnet_val_arr3, nullptr); + + //Check xpins + for (int i = 0; i < org_n3+1; ++i) + EXPECT_EQ(org_xpins3[i], written_xpins3[i]); + + //Check pins + for (int i = 0; i < org_m3; ++i) + EXPECT_EQ(org_pins3[i], written_pins3[i]); + + //Check edge weights + auto org_netWeightsVal3 = org_netWeights3->get_vals(); + auto written_netWeightsVal3 = written_netWeights3->get_vals(); + for (int i = 0; i < org_n3; ++i) + EXPECT_EQ(org_netWeightsVal3[i], written_netWeightsVal3[i]); + + //Check cell weights + auto org_cellWeightsVal3 = org_cellWeights3->get_vals(); + auto written_cellWeightsVal3 = written_cellWeights3->get_vals(); + + for (int i = 0; i < org_vertex_size3; ++i) + EXPECT_EQ(org_cellWeightsVal3[i], written_cellWeightsVal3[i]); + + // Check xnets + for (int i = 0; i ReadHyperGraph4("HyperGraph_With_Cell_and_Nets_Weight.hypeg"); + sparsebase::object::HyperGraph* org_hypergraph4 = ReadHyperGraph4.ReadHyperGraph(); + sparsebase::format::Format *org_con4 = org_hypergraph4->get_connectivity(); + int org_n4 = org_hypergraph4->n_; + int org_m4 = org_hypergraph4->m_; + int org_constraint_num4 = org_hypergraph4->constraint_num_; + int org_base_type4 = org_hypergraph4->base_type_; + int org_vertex_size4 = org_con4->get_dimensions()[1]; + auto org_xpins4 = org_con4->AsAbsolute>()->get_row_ptr(); + auto org_pins4 = org_con4->AsAbsolute>()->get_col(); + auto org_xpin_val_arr4 = org_con4->AsAbsolute>()->get_vals(); + auto org_xNetsCSR4 = org_hypergraph4->xNetCSR_; + auto org_xnets4 = org_xNetsCSR4->get_row_ptr(); + auto org_cells4 = org_xNetsCSR4->get_col(); + auto org_xnet_val_arr4 = org_xNetsCSR4->get_vals(); + auto org_netWeights4 = org_hypergraph4->netWeights_; + auto org_cellWeights4 = org_hypergraph4->cellWeights_; + + //Write the original graph4 + sparsebase::io::PatohWriter WriteHyperGraph4("org_hypergraph4.hypeg",false,true,true); + WriteHyperGraph4.WriteHyperGraph(org_hypergraph4); + + //Read the output of writer + sparsebase::io::PatohReader Read_Written_HyperGraph4("org_hypergraph4.hypeg"); + sparsebase::object::HyperGraph* written_hypergraph4 = Read_Written_HyperGraph4.ReadHyperGraph(); + sparsebase::format::Format *written_con4 = written_hypergraph4->get_connectivity(); + + int written_n4 = written_hypergraph4->n_; + int written_m4 = written_hypergraph4->m_; + int written_constraint_num4 = written_hypergraph4->constraint_num_; + int written_base_type4 = written_hypergraph4->base_type_; + int written_vertex_size4 = written_con4->get_dimensions()[1]; + auto written_xpins4 = written_con4->AsAbsolute>()->get_row_ptr(); + auto written_pins4 = written_con4->AsAbsolute>()->get_col(); + auto written_xpin_val_arr4 = written_con4->AsAbsolute>()->get_vals(); + auto written_xNetsCSR4 = written_hypergraph4->xNetCSR_; + auto written_xnets4 = written_xNetsCSR4->get_row_ptr(); + auto written_cells4 = written_xNetsCSR4->get_col(); + auto written_xnet_val_arr4 = written_xNetsCSR4->get_vals(); + auto written_netWeights4 = written_hypergraph4->netWeights_; + auto written_cellWeights4 = written_hypergraph4->cellWeights_; + + //Compare the original hypergraph 4 with the written version + //Check the dimensions + EXPECT_EQ(org_n4,written_n4); + EXPECT_EQ(org_vertex_size4, written_vertex_size4); + EXPECT_EQ(org_m4,written_m4); + EXPECT_EQ(org_constraint_num4,written_constraint_num4); + EXPECT_EQ(org_base_type4,written_base_type4); + + EXPECT_EQ(written_xpin_val_arr4, nullptr); + EXPECT_EQ(written_xnet_val_arr4, nullptr); + + //Check xpins + for (int i = 0; i < org_n4+1; ++i) + EXPECT_EQ(org_xpins4[i], written_xpins4[i]); + + //Check pins + for (int i = 0; i < org_m4; ++i) + EXPECT_EQ(org_pins4[i], written_pins4[i]); + + //Check edge weights + auto org_netWeightsVal4 = org_netWeights4->get_vals(); + auto written_netWeightsVal4 = written_netWeights4->get_vals(); + for (int i = 0; i < org_n4; ++i) + EXPECT_EQ(org_netWeightsVal4[i], written_netWeightsVal4[i]); + + //Check cell weights + auto org_cellWeightsVal4 = org_cellWeights4->get_vals(); + auto written_cellWeightsVal4 = written_cellWeights4->get_vals(); + + for (int i = 0; i < org_vertex_size4; ++i) + EXPECT_EQ(org_cellWeightsVal4[i], written_cellWeightsVal4[i]); + + // Check xnets + for (int i = 0; i>> print(csr_array((vals, col, row_ptr), shape=(5,5)).toarray()) + +Output: [[0. 0. 0. 0. 0. ] + [0.1 0. 0. 0. 0. ] + [0. 0.3 0. 0. 0. ] + [0.2 0. 0. 0. 0. ] + [0. 0. 0.4 0.5 0. ]] +*/ + +////////// Symmetric sparse matrix +//// Raw pointers int row_ptr_symm[6]{0, 3, 5, 7, 9, 11}; int row_symm[11]{0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4}; int col_symm[11]{0, 1, 3, 0, 2, 1, 4, 0, 4, 2, 3}; float vals_symm[11]{0.7, 0.1, 0.2, 0.1, 0.3, 0.3, 0.4, 0.2, 0.5, 0.4, 0.5}; +//// Scipy output +/* COO +>>> print(coo_array((vals_symm, (row_symm, col_symm)), shape=(5,5)).toarray()) + +Output: [[0.7 0.1 0. 0.2 0. ] + [0.1 0. 0.3 0. 0. ] + [0. 0.3 0. 0. 0.4] + [0.2 0. 0. 0. 0.5] + [0. 0. 0.4 0.5 0. ]] +*/ +/* CSR +>>> print(csr_array((vals_symm, col_symm, row_ptr_symm), shape=(5,5)).toarray()) + +Output: [[0.7 0.1 0. 0.2 0. ] + [0.1 0. 0.3 0. 0. ] + [0. 0.3 0. 0. 0.4] + [0.2 0. 0. 0. 0.5] + [0. 0. 0.4 0.5 0. ]] +*/ + +////////// Skew-symmetric sparse matrix +//// Raw pointers +int row_skew_symm[10]{0, 0, 1, 1, 2, 2, 3, 3, 4, 4}; +int col_skew_symm[10]{1, 3, 0, 2, 1, 4, 0, 4, 2, 3}; +float vals_skew_symm[10]{-0.1, -0.2, 0.1, -0.3, 0.3, -0.4, 0.2, -0.5, 0.4, 0.5}; +//// Scipy output +/* COO +>>> print(coo_array((vals_skew_symm, (row_skew_symm, col_skew_symm)), shape=(5,5)).toarray()) + +Output: [[ 0. -0.1 0. -0.2 0. ] + [ 0.1 0. -0.3 0. 0. ] + [ 0. 0.3 0. 0. -0.4] + [ 0.2 0. 0. 0. -0.5] + [ 0. 0. 0.4 0.5 0. ]] +*/ +/* +Script used for (for reference): +``` python +import numpy as np +from scipy.sparse import coo_array, csr_array +row_ptr = np.array([0, 0, 1, 2, 3, 5]) +row = np.array([1, 2, 3, 4, 4]) +col = np.array([0, 1, 0, 2, 3]) +vals = np.array([0.1, 0.3, 0.2, 0.4, 0.5]) +row_ptr_symm = np.array([0, 3, 5, 7, 9, 11]) +row_symm = np.array([0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4]) +col_symm = np.array([0, 1, 3, 0, 2, 1, 4, 0, 4, 2, 3]) +vals_symm = np.array([0.7, 0.1, 0.2, 0.1, 0.3, 0.3, 0.4, 0.2, 0.5, 0.4, 0.5]) +row_skew_symm = np.array([0, 0, 1, 1, 2, 2, 3, 3, 4, 4]) +col_skew_symm = np.array([1, 3, 0, 2, 1, 4, 0, 4, 2, 3]) +vals_skew_symm = np.array([-0.1, -0.2, 0.1, -0.3, 0.3, -0.4, 0.2, -0.5, 0.4, 0.5]) + +(coo_array((vals, (row, col)), shape=(5,5)).toarray()) +(csr_array((vals, col, row_ptr), shape=(5,5)).toarray()) +(coo_array((vals_symm, (row_symm, col_symm)), shape=(5,5)).toarray()) +(csr_array((vals_symm, col_symm, row_ptr_symm), shape=(5,5)).toarray()) + +(coo_array((vals_skew_symm, (row_skew_symm, col_skew_symm)), shape=(5,5)).toarray()) +``` + +*/ + +float one_row_one_col_vals[10]{0, 0.1, 0, 0.3, 0.2, 0.4, 0, 0, 0.5, 0}; +int one_row_one_col_length = 10; + const std::string mtx_data = R"(%%MatrixMarket matrix coordinate pattern general %This is a comment @@ -21,6 +112,15 @@ const std::string mtx_data = 5 3 5 4 )"; +/* Scipy output (adjacency matrix): +array([[0., 0., 0., 0., 0.], + [1., 0., 0., 0., 0.], + [0., 1., 0., 0., 0.], + [1., 0., 0., 0., 0.], + [0., 0., 1., 1., 0.]]) +*/ +// CSR = (row_ptr, col, nullptr) +// COO = (row, col, nullptr) const std::string mtx_symm_data = R"(%%MatrixMarket matrix coordinate pattern symmetric @@ -33,6 +133,34 @@ const std::string mtx_symm_data = 5 3 5 4 )"; +/* Scipy output: +array([[1., 1., 0., 1., 0.], + [1., 0., 1., 0., 0.], + [0., 1., 0., 0., 1.], + [1., 0., 0., 0., 1.], + [0., 0., 1., 1., 0.]]) +*/ +// CSR = (row_ptr_symm, col_symm, nullptr) +// COO = (row_symm, col_symm, nullptr) + +const std::string mtx_skew_symm_data = + R"(%%MatrixMarket matrix coordinate pattern skew-symmetric +%This is a comment +5 5 5 +2 1 +4 1 +3 2 +5 3 +5 4 +)"; +/* Scipy output (coo.A): +array([[ 0., -1., 0., -1., 0.], + [ 1., 0., -1., 0., 0.], + [ 0., 1., 0., 0., -1.], + [ 1., 0., 0., 0., -1.], + [ 0., 0., 1., 1., 0.]]) +*/ +// COO = (row_skew-symm, col_skew_symm, nullptr) const std::string mtx_data_with_values = R"(%%MatrixMarket matrix coordinate real general @@ -44,68 +172,95 @@ const std::string mtx_data_with_values = 5 3 0.4 5 4 0.5 )"; +/* Scipy output: +array([[0. , 0. , 0. , 0. , 0. ], + [0.1, 0. , 0. , 0. , 0. ], + [0. , 0.3, 0. , 0. , 0. ], + [0.2, 0. , 0. , 0. , 0. ], + [0. , 0. , 0.4, 0.5, 0. ]]) +*/ +// COO = (row, col, vals)) +// CSR = (row_ptr, col, vals)) const std::string mtx_data_with_values_array = R"(%%MatrixMarket matrix array real general %This is a comment 5 5 0 +0.1 0 +0.2 0 0 0 -0.1 +0.3 0 0 0 0 0 -0.3 0 +0.4 0 0 -0.2 0 0 +0.5 0 0 0 0 -0.4 -0.5 0 )"; +/* Scipy output: +array([[0. , 0. , 0. , 0. , 0. ], + [0.1, 0. , 0. , 0. , 0. ], + [0. , 0.3, 0. , 0. , 0. ], + [0.2, 0. , 0. , 0. , 0. ], + [0. , 0. , 0.4, 0.5, 0. ]]) +*/ +// COO = (row, col, vals)) +// CSR = (row_ptr, col, vals)) const std::string mtx_data_array = R"(%%MatrixMarket matrix array real general %This is a comment 5 5 0 +0.1 0 +0.2 0 0 0 -0.1 +0.3 0 0 0 0 0 -0.3 0 +0.4 0 0 -0.2 0 0 +0.5 0 0 0 0 -0.4 -0.5 0 )"; +/* Scipy output: +array([[0. , 0. , 0. , 0. , 0. ], + [0.1, 0. , 0. , 0. , 0. ], + [0. , 0.3, 0. , 0. , 0. ], + [0.2, 0. , 0. , 0. , 0. ], + [0. , 0. , 0.4, 0.5, 0. ]]) +*/ +// COO = (row, col, vals)) +// CSR = (row_ptr, col, vals)) const std::string mtx_symm_data_with_values = R"(%%MatrixMarket matrix coordinate real symmetric @@ -118,6 +273,34 @@ const std::string mtx_symm_data_with_values = 5 3 0.4 5 4 0.5 )"; +/* Scipy output: +array([[0.7, 0.1, 0. , 0.2, 0. ], + [0.1, 0. , 0.3, 0. , 0. ], + [0. , 0.3, 0. , 0. , 0.4], + [0.2, 0. , 0. , 0. , 0.5], + [0. , 0. , 0.4, 0.5, 0. ]]) +*/ +// COO = (row_ptr_symm, col_symm, vals_symm)) +// CSR = (row_symm, col_symm, vals_symm)) + +const std::string mtx_skew_symm_data_with_values = + R"(%%MatrixMarket matrix coordinate real skew-symmetric +%This is a comment +5 5 5 +2 1 0.1 +4 1 0.2 +3 2 0.3 +5 3 0.4 +5 4 0.5 +)"; +/* Scipy output: +array([[ 0. , -0.1, 0. , -0.2, 0. ], + [ 0.1, 0. , -0.3, 0. , 0. ], + [ 0. , 0.3, 0. , 0. , -0.4], + [ 0.2, 0. , 0. , 0. , -0.5], + [ 0. , 0. , 0.4, 0.5, 0. ]]) +*/ +// COO = (row_skew_symm, col_skew_symm, vals_skew_symm) const std::string mtx_data_one_col_with_values = R"(%%MatrixMarket matrix coordinate real general @@ -129,6 +312,18 @@ const std::string mtx_data_one_col_with_values = 6 1 0.4 9 1 0.5 )"; +/* Scipy output: +array([[0. ], + [0.1], + [0. ], + [0.3], + [0.2], + [0.4], + [0. ], + [0. ], + [0.5], + [0. ]]) +*/ const std::string mtx_data_one_row_with_values = R"(%%MatrixMarket matrix coordinate real general @@ -140,6 +335,9 @@ const std::string mtx_data_one_row_with_values = 1 6 0.4 1 9 0.5 )"; +/* Scipy output: +array([[0. , 0.1, 0. , 0.3, 0.2, 0.4, 0. , 0. , 0.5, 0. ]]) +*/ const std::string mtx_array_data_one_col_with_values = R"(%%MatrixMarket matrix array real general @@ -156,6 +354,18 @@ const std::string mtx_array_data_one_col_with_values = 0.5 0 )"; +/* Scipy output: +array([[0. ], + [0.1], + [0. ], + [0.3], + [0.2], + [0.4], + [0. ], + [0. ], + [0.5], + [0. ]]) +*/ const std::string mtx_array_data_one_row_with_values = R"(%%MatrixMarket matrix array real general @@ -172,6 +382,9 @@ const std::string mtx_array_data_one_row_with_values = 0.5 0 )"; +/* Scipy output: +array([[0. , 0.1, 0. , 0.3, 0.2, 0.4, 0. , 0. , 0.5, 0. ]]) +*/ const std::string edge_list_data = R"(1 0 3 0 2 1 @@ -185,3 +398,137 @@ const std::string edge_list_data_with_values = R"(1 0 0.1 4 2 0.4 4 3 0.5 )"; + +//Metis Graph Reader + +//Data_1: https://people.sc.fsu.edu/~jburkardt/data/metis_graph/tiny_03.graph +int metis_n_1 = 7; +int metis_m_1 = 22; +int metis_ncon_1 = 1; +int metis_row_1[22]{1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7}; +int metis_col_1[22]{2, 3, 5, 1, 3, 4, 1, 2, 4, 5, 2, 3, 6, 7, 1, 3, 6, 4, 5, 7, 4, 6}; +int metis_val_1[22]{1, 2, 1, 1, 2, 1, 2, 2, 2, 3, 1, 2, 2, 5, 1, 3, 2, 2, 2, 6, 5, 6}; +int metis_vertex_weights_1[8]{0, 4, 2, 5, 3, 1, 6, 2}; + +const std::string metis_graph_1 = R"( 7 11 11 +4 5 1 3 2 2 1 +2 1 1 3 2 4 1 +5 5 3 4 2 2 2 1 2 +3 2 1 3 2 6 2 7 5 +1 1 1 3 3 6 2 +6 5 2 4 2 7 6 +2 6 6 4 5 +)"; + +//Data_2: https://people.sc.fsu.edu/~jburkardt/data/metis_graph/tiny_04.graph +int metis_n_2 = 7; +int metis_m_2 = 22; +int metis_ncon_2 = 3; +int metis_row_2[22]{1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7}; +int metis_col_2[22]{2, 3, 5, 1, 3, 4, 1, 2, 4, 5, 2, 3, 6, 7, 1, 3, 6, 4, 5, 7, 4, 6}; +int metis_vertex_weights_2[8][3]{{0, 0, 0}, {1, 2, 0}, {0, 2, 2}, {4, 1, 1}, {2, 2, 3}, {1, 1, 1}, {2, 2, 1}, {1, 2, 1}}; +const std::string metis_graph_2 = R"( 7 11 10 3 +1 2 0 5 3 2 +0 2 2 1 3 4 +4 1 1 5 4 2 1 +2 2 3 2 3 6 7 +1 1 1 1 3 6 +2 2 1 5 4 7 +1 2 1 6 4 +)"; + + +//PATOH HyperGraph Reader + +int hypergraph1_n = 11; +int hypergraph1_m = 31; +int hypergraph1_constraint_num = 1; +int hypergraph1_base_type = 0; +int hypergraph1_vertex_size = 12; +int hypergraph1_xpins[12]{0,5,7,11,13,15,19,21,25,27,29,31}; +int hypergraph1_pins[31]{2, 3, 5, 6, 9, 0, 1, 0, 1, 2, 3, 1, 3, 4, 5, 4, 5, 6, 7, 6, 7, 8, 9, 10, 11, 8, 10, 8, 11, 2, 5}; +int hypergraph1_netWeights[11]{1,1,1,1,1,1,1,1,1,1,1}; +int hypergraph1_cellWeights[12]{1,1,1,1,1,1,1,1,1,1,1,1}; +int hypergraph1_xnets[13]{0,2,5,8,11,13,17,20,22,25,27,29,31}; +int hypergraph1_cells[31]{1, 2, 1, 2, 3, 0, 2, 10, 0, 2, 3, 4, 5, 0, 4, 5, 10, 0, 5, 6, 5, 6, 7, 8, 9, 0, 7, 7, 8, 7, 9}; + +const std::string hypergraph_1 = R"(% base:(0/1) #cells #nets #pins +0 12 11 31 +% pins of each net in the hypergraph +2 3 5 6 9 +0 1 +0 1 2 3 +1 3 +4 5 +4 5 6 7 +6 7 +8 9 10 11 +8 10 +8 11 +2 5)"; + +int hypergraph2_n = 9; +int hypergraph2_m = 28; +int hypergraph2_vertex_size = 8; +int hypergraph2_xpins[10]{0,5,9,13,15,17,20,23,26,28}; +int hypergraph2_pins[28]{8,6,3,5,2,4,5,1,7,4,2,5,7,4,7,3,5,8,2,4,6,5,2,5,7,2,8,4}; +int hypergraph2_netWeights[9]{10,15,13,18,25,20,14,27,29}; +int hypergraph2_cellWeights[8]{1,1,1,1,1,1,1,1}; +int hypergraph2_xnets[9]{0,1,6,8,13,19,21,25,28}; +int hypergraph2_cells[28]{2,1,3,6,7,8,1,5,2,3,4,6,9,1,2,3,5,7,8,1,7,2,3,4,8,1,6,9}; + +const std::string hypergraph_2 = R"(% base:(0/1) #cells #nets #pins +1 8 9 28 2 +10 8 6 3 5 2 +15 4 5 1 7 +13 4 2 5 7 +18 4 7 +25 3 5 +20 8 2 4 +14 6 5 2 +27 5 7 2 +29 8 4)"; + +int hypergraph3_n = 9; +int hypergraph3_m = 28; +int hypergraph3_vertex_size = 8; +int hypergraph3_xpins[10]{0,5,9,13,15,17,20,23,26,28}; +int hypergraph3_pins[28]{8,6,3,5,2,4,5,1,7,4,2,5,7,4,7,3,5,8,2,4,6,5,2,5,7,2,8,4}; +int hypergraph3_netWeights[9]{1,1,1,1,1,1,1,1,1}; +int hypergraph3_cellWeights[8]{80,85,30,55,42,39,90,102}; +int hypergraph3_xnets[9]{0,1,6,8,13,19,21,25,28}; +int hypergraph3_cells[28]{2,1,3,6,7,8,1,5,2,3,4,6,9,1,2,3,5,7,8,1,7,2,3,4,8,1,6,9}; + +const std::string hypergraph_3 = R"(1 8 9 28 1 +8 6 3 5 2 +4 5 1 7 +4 2 5 7 +4 7 +3 5 +8 2 4 +6 5 2 +5 7 2 +8 4 +80 85 30 55 42 39 90 102)"; + +int hypergraph4_n = 9; +int hypergraph4_m = 28; +int hypergraph4_vertex_size = 8; +int hypergraph4_xpins[10]{0,5,9,13,15,17,20,23,26,28}; +int hypergraph4_pins[28]{8,6,3,5,2,4,5,1,7,4,2,5,7,4,7,3,5,8,2,4,6,5,2,5,7,2,8,4}; +int hypergraph4_netWeights[9]{10,15,13,18,25,20,14,27,29}; +int hypergraph4_cellWeights[8]{80,85,30,55,42,39,90,102}; +int hypergraph4_xnets[9]{0,1,6,8,13,19,21,25,28}; +int hypergraph4_cells[28]{2,1,3,6,7,8,1,5,2,3,4,6,9,1,2,3,5,7,8,1,7,2,3,4,8,1,6,9}; + +const std::string hypergraph_4 = R"(1 8 9 28 3 +10 8 6 3 5 2 +15 4 5 1 7 +13 4 2 5 7 +18 4 7 +25 3 5 +20 8 2 4 +14 6 5 2 +27 5 7 2 +29 8 4 +80 85 30 55 42 39 90 102)"; \ No newline at end of file diff --git a/tests/suites/sparsebase/reorder/boba_reorder_tests.cc b/tests/suites/sparsebase/reorder/boba_reorder_tests.cc new file mode 100644 index 00000000..24e989f8 --- /dev/null +++ b/tests/suites/sparsebase/reorder/boba_reorder_tests.cc @@ -0,0 +1,26 @@ +#include +#include + +#include "gtest/gtest.h" +#include "sparsebase/bases/reorder_base.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/format/format_order_one.h" +#include "sparsebase/format/format_order_two.h" +#include "sparsebase/reorder/gray_reorder.h" +#include "sparsebase/reorder/slashburn_reorder.h" +#include "sparsebase/reorder/reorderer.h" + +const std::string FILE_NAME = "../../../../examples/data/ash958.mtx"; + +using namespace sparsebase; +; +using namespace sparsebase::reorder; +using namespace sparsebase::bases; +#include "../functionality_common.inc" +TEST(BOBAReorderTest, BasicTest) { + BOBAReorder reorder; + auto order = reorder.GetReorder(&global_coo, {&cpu_context}, true); + check_reorder(order, n); +} diff --git a/tests/suites/sparsebase/reorder/slashburn_reorder_tests.cc b/tests/suites/sparsebase/reorder/slashburn_reorder_tests.cc new file mode 100644 index 00000000..1e73498c --- /dev/null +++ b/tests/suites/sparsebase/reorder/slashburn_reorder_tests.cc @@ -0,0 +1,36 @@ +#include +#include + +#include "gtest/gtest.h" +#include "sparsebase/bases/reorder_base.h" +#include "sparsebase/format/coo.h" +#include "sparsebase/format/csc.h" +#include "sparsebase/format/csr.h" +#include "sparsebase/format/format_order_one.h" +#include "sparsebase/format/format_order_two.h" +#include "sparsebase/reorder/gray_reorder.h" +#include "sparsebase/reorder/slashburn_reorder.h" +#include "sparsebase/reorder/reorderer.h" + +const std::string FILE_NAME = "../../../../examples/data/ash958.mtx"; + +using namespace sparsebase; +; +using namespace sparsebase::reorder; +using namespace sparsebase::bases; +#include "../functionality_common.inc" +TEST(SlashburnReorderTest, BasicTest) { + SlashburnReorder reorder(1, false, false); + auto order = reorder.GetReorder(&global_coo, {&cpu_context}, true); + check_reorder(order, n); +} +TEST(SlashburnReorderTest, BasicTestMultiK) { + SlashburnReorder reorder(10, false, false); + auto order = reorder.GetReorder(&global_coo, {&cpu_context}, true); + check_reorder(order, n); +} +TEST(SlashburnReorderTest, BasicTestGreedyHub) { + SlashburnReorder reorder(1, true, true); + auto order = reorder.GetReorder(&global_coo, {&cpu_context}, true); + check_reorder(order, n); +}