From 04d73c8f63b87160e85ba3e8daac2ce42ed89346 Mon Sep 17 00:00:00 2001 From: haolan Date: Mon, 29 May 2023 07:59:04 +0000 Subject: [PATCH 1/4] add Bernoulli CRS in C++ libtorch --- include/AMMBench.h | 1 + include/CPPAlgos/BCRSCPPAlgo.h | 57 ++++++++++++++++++++++++++++++++++ src/CPPAlgos/BCRSCPPAlgo.cpp | 35 +++++++++++++++++++++ src/CPPAlgos/CMakeLists.txt | 1 + src/CPPAlgos/CPPAlgoTable.cpp | 4 ++- test/SystemTest/CRSTest.cpp | 11 +++++++ 6 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 include/CPPAlgos/BCRSCPPAlgo.h create mode 100644 src/CPPAlgos/BCRSCPPAlgo.cpp diff --git a/include/AMMBench.h b/include/AMMBench.h index 757fffea..b3b99317 100755 --- a/include/AMMBench.h +++ b/include/AMMBench.h @@ -100,6 +100,7 @@ #include #include #include +#include /** * @} * diff --git a/include/CPPAlgos/BCRSCPPAlgo.h b/include/CPPAlgos/BCRSCPPAlgo.h new file mode 100644 index 00000000..eb1e6b6f --- /dev/null +++ b/include/CPPAlgos/BCRSCPPAlgo.h @@ -0,0 +1,57 @@ +// +// Created by haolan on 5/29/23. +// + +#ifndef INTELLISTREAM_BCRSCPPALGO_H +#define INTELLISTREAM_BCRSCPPALGO_H +#include + +namespace AMMBench { +/** + * @ingroup AMMBENCH_CppAlgos The algorithms writtrn in c++ + * @{ + */ +/** + * @class CRSCPPlgo CPPAlgos/BCRSCPPAlgo.h + * @brief The cloumn row sampling (CRS) class of c++ algos + * + */ + class BCRSCPPAlgo : public AMMBench::AbstractCPPAlgo { + public: + BCRSCPPAlgo() { + + } + + ~BCRSCPPAlgo() { + + } + + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketc or sampling + * @return the output c matrix + */ + virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, int sketchSize); + + }; + +/** + * @ingroup AMMBENCH_CppAlgos + * @typedef AbstractMatrixCppAlgoPtr + * @brief The class to describe a shared pointer to @ref BCRSCppAlgo + + */ + typedef std::shared_ptr BCRSCPPAlgoPtr; +/** + * @ingroup AMMBENCH_CppAlgos + * @def newBCRSCppAlgo + * @brief (Macro) To creat a new @ref BCRSCppAlgounder shared pointer. + */ +#define newBCRSCPPAlgo std::make_shared +} +/** + * @} + */ +#endif //INTELLISTREAM_BCRSCPPALGO_H diff --git a/src/CPPAlgos/BCRSCPPAlgo.cpp b/src/CPPAlgos/BCRSCPPAlgo.cpp new file mode 100644 index 00000000..6398cfa5 --- /dev/null +++ b/src/CPPAlgos/BCRSCPPAlgo.cpp @@ -0,0 +1,35 @@ +// +// Created by haolan on 5/29/23. +// +#include + +namespace AMMBench { +torch::Tensor BCRSCPPAlgo::amm(torch::Tensor A, torch::Tensor B, int k) { + A = A.t(); + auto A_size = A.sizes(); + int64_t n = A_size[0]; + //int64_t m = A_size[1]; + + assert(n == B.size(0)); + //TORCH_CHECK(n == B.size(0)); + //TORCH_CHECK(k < n); + + //INTELLI_INFO("Running Bernoulli CRS CPP"); + + // probability distribution + torch::Tensor sample = torch::rand({n}); // default: uniform + sample = sample.div(sample.sum() / k); // sum = k as per the paper + + // diagonal scaling matrix P (nxn) + torch::Tensor P = torch::diag(1.0 / torch::sqrt(sample)); + + // random diagonal sampling matrix K (nxn) + sample = (torch::rand({n}) < sample).to(torch::kFloat32); + torch::Tensor K = torch::diag(sample); + + torch::Tensor a = torch::matmul(torch::matmul(A.t(), P), K); + torch::Tensor b = torch::matmul(torch::matmul(a, K), P); + + return torch::matmul(b, B); +} +} \ No newline at end of file diff --git a/src/CPPAlgos/CMakeLists.txt b/src/CPPAlgos/CMakeLists.txt index 79f5a9d4..0fa2f4d2 100644 --- a/src/CPPAlgos/CMakeLists.txt +++ b/src/CPPAlgos/CMakeLists.txt @@ -3,4 +3,5 @@ add_sources( CRSCPPAlgo.cpp CPPAlgoTable.cpp CRSV2CPPAlgo.cpp + BCRSCPPAlgo.cpp ) \ No newline at end of file diff --git a/src/CPPAlgos/CPPAlgoTable.cpp b/src/CPPAlgos/CPPAlgoTable.cpp index af137843..2b5da8e8 100644 --- a/src/CPPAlgos/CPPAlgoTable.cpp +++ b/src/CPPAlgos/CPPAlgoTable.cpp @@ -5,11 +5,13 @@ #include #include #include +#include namespace AMMBench { AMMBench::CPPAlgoTable::CPPAlgoTable() { algoMap["mm"] = newAbstractCPPAlgo(); algoMap["crs"] = newCRSCPPAlgo(); - algoMap["crsV2"] = newCRSV2CPPAlgo(); + algoMap["crsV2"] = newCRSV2CPPAlgo(); + algoMap["bcrs"] = newBCRSCPPAlgo(); } } // AMMBench \ No newline at end of file diff --git a/test/SystemTest/CRSTest.cpp b/test/SystemTest/CRSTest.cpp index 33c4add2..1208968f 100644 --- a/test/SystemTest/CRSTest.cpp +++ b/test/SystemTest/CRSTest.cpp @@ -82,4 +82,15 @@ TEST_CASE("Test CRS v2 in cpp", "[short]") auto ammC = crs.amm(A, B, 20); double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); REQUIRE(froError < 0.5); +} +TEST_CASE("Test Bernoulli CRS in cpp", "[short]") +{ + torch::manual_seed(114514); + AMMBench::BCRSCPPAlgo bcrs; + auto A = torch::rand({400, 400}); + auto B = torch::rand({400, 400}); + auto realC = torch::matmul(A, B); + auto ammC = bcrs.amm(A, B, 20); + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); + REQUIRE(froError < 0.5); } \ No newline at end of file From 64ed6011cbfec485e2283fae7c6725242a0edba3 Mon Sep 17 00:00:00 2001 From: haolan Date: Mon, 29 May 2023 08:23:43 +0000 Subject: [PATCH 2/4] add Element Wise Sampling in C++ libtorch --- include/AMMBench.h | 1 + include/CPPAlgos/AbstractCPPAlgo.h | 2 +- include/CPPAlgos/BCRSCPPAlgo.h | 4 +-- include/CPPAlgos/CRSCPPAlgo.h | 4 +-- include/CPPAlgos/CRSV2CPPAlgo.h | 4 +-- include/CPPAlgos/EWSCPPAlgo.h | 57 ++++++++++++++++++++++++++++++ src/CPPAlgos/CMakeLists.txt | 1 + src/CPPAlgos/CPPAlgoTable.cpp | 2 ++ src/CPPAlgos/EWSCPPAlgo.cpp | 34 ++++++++++++++++++ test/CMakeLists.txt | 1 + test/SystemTest/EWSTest.cpp | 11 ++++++ 11 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 include/CPPAlgos/EWSCPPAlgo.h create mode 100644 src/CPPAlgos/EWSCPPAlgo.cpp diff --git a/include/AMMBench.h b/include/AMMBench.h index b3b99317..694131e7 100755 --- a/include/AMMBench.h +++ b/include/AMMBench.h @@ -101,6 +101,7 @@ #include #include #include +#include /** * @} * diff --git a/include/CPPAlgos/AbstractCPPAlgo.h b/include/CPPAlgos/AbstractCPPAlgo.h index eebc94cb..a5b29a1b 100644 --- a/include/CPPAlgos/AbstractCPPAlgo.h +++ b/include/CPPAlgos/AbstractCPPAlgo.h @@ -13,7 +13,7 @@ #include namespace AMMBench { /** - * @ingroup AMMBENCH_CppAlgos The algorithms writtrn in c++ + * @ingroup AMMBENCH_CppAlgos The algorithms written in c++ * @{ */ /** diff --git a/include/CPPAlgos/BCRSCPPAlgo.h b/include/CPPAlgos/BCRSCPPAlgo.h index eb1e6b6f..a9452522 100644 --- a/include/CPPAlgos/BCRSCPPAlgo.h +++ b/include/CPPAlgos/BCRSCPPAlgo.h @@ -8,12 +8,12 @@ namespace AMMBench { /** - * @ingroup AMMBENCH_CppAlgos The algorithms writtrn in c++ + * @ingroup AMMBENCH_CppAlgos The algorithms written in c++ * @{ */ /** * @class CRSCPPlgo CPPAlgos/BCRSCPPAlgo.h - * @brief The cloumn row sampling (CRS) class of c++ algos + * @brief The Bernoulli column row sampling (CRS) class of c++ algos * */ class BCRSCPPAlgo : public AMMBench::AbstractCPPAlgo { diff --git a/include/CPPAlgos/CRSCPPAlgo.h b/include/CPPAlgos/CRSCPPAlgo.h index bbb7b6a6..324e2c0f 100644 --- a/include/CPPAlgos/CRSCPPAlgo.h +++ b/include/CPPAlgos/CRSCPPAlgo.h @@ -8,12 +8,12 @@ #include namespace AMMBench { /** - * @ingroup AMMBENCH_CppAlgos The algorithms writtrn in c++ + * @ingroup AMMBENCH_CppAlgos The algorithms written in c++ * @{ */ /** * @class CRSCPPlgo CPPAlgos/CRSCPPAlgo.h - * @brief The cloumn row sampling (CRS) class of c++ algos + * @brief The column row sampling (CRS) class of c++ algos * */ class CRSCPPAlgo : public AMMBench::AbstractCPPAlgo { diff --git a/include/CPPAlgos/CRSV2CPPAlgo.h b/include/CPPAlgos/CRSV2CPPAlgo.h index c83fedc5..389094e6 100644 --- a/include/CPPAlgos/CRSV2CPPAlgo.h +++ b/include/CPPAlgos/CRSV2CPPAlgo.h @@ -9,12 +9,12 @@ namespace AMMBench { /** - * @ingroup AMMBENCH_CppAlgos The algorithms writtrn in c++ + * @ingroup AMMBENCH_CppAlgos The algorithms written in c++ * @{ */ /** * @class CRSCPPlgo CPPAlgos/CRSV2CPPAlgo.h - * @brief The cloumn row sampling (CRS) class of c++ algos + * @brief The column row sampling (CRS) class of c++ algos, a second implementation * */ class CRSV2CPPAlgo : public AMMBench::AbstractCPPAlgo { diff --git a/include/CPPAlgos/EWSCPPAlgo.h b/include/CPPAlgos/EWSCPPAlgo.h new file mode 100644 index 00000000..09e198d1 --- /dev/null +++ b/include/CPPAlgos/EWSCPPAlgo.h @@ -0,0 +1,57 @@ +// +// Created by haolan on 5/29/23. +// + +#ifndef INTELLISTREAM_EWSCPPALGO_H +#define INTELLISTREAM_EWSCPPALGO_H +#include + +namespace AMMBench { +/** + * @ingroup AMMBENCH_CppAlgos The algorithms written in c++ + * @{ + */ +/** + * @class CPPAlgos/EWSCPPAlgo.h + * @brief The Element Wise Sampling (EWS) class of c++ algos + * + */ + class EWSCPPAlgo : public AMMBench::AbstractCPPAlgo { + public: + EWSCPPAlgo() { + + } + + ~EWSCPPAlgo() { + + } + + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketc or sampling + * @return the output c matrix + */ + virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, int sketchSize); + + }; + +/** + * @ingroup AMMBENCH_CppAlgos + * @typedef AbstractMatrixCppAlgoPtr + * @brief The class to describe a shared pointer to @ref EWSCppAlgo + + */ + typedef std::shared_ptr EWSCPPAlgoPtr; +/** + * @ingroup AMMBENCH_CppAlgos + * @def newEWSCppAlgo + * @brief (Macro) To creat a new @ref EWSCppAlgounder shared pointer. + */ +#define newEWSCPPAlgo std::make_shared +} +/** + * @} + */ +#endif //INTELLISTREAM_EWSCPPALGO_H diff --git a/src/CPPAlgos/CMakeLists.txt b/src/CPPAlgos/CMakeLists.txt index 0fa2f4d2..5368a3c6 100644 --- a/src/CPPAlgos/CMakeLists.txt +++ b/src/CPPAlgos/CMakeLists.txt @@ -4,4 +4,5 @@ add_sources( CPPAlgoTable.cpp CRSV2CPPAlgo.cpp BCRSCPPAlgo.cpp + EWSCPPAlgo.cpp ) \ No newline at end of file diff --git a/src/CPPAlgos/CPPAlgoTable.cpp b/src/CPPAlgos/CPPAlgoTable.cpp index 2b5da8e8..99fb7f56 100644 --- a/src/CPPAlgos/CPPAlgoTable.cpp +++ b/src/CPPAlgos/CPPAlgoTable.cpp @@ -6,12 +6,14 @@ #include #include #include +#include namespace AMMBench { AMMBench::CPPAlgoTable::CPPAlgoTable() { algoMap["mm"] = newAbstractCPPAlgo(); algoMap["crs"] = newCRSCPPAlgo(); algoMap["crsV2"] = newCRSV2CPPAlgo(); algoMap["bcrs"] = newBCRSCPPAlgo(); + algoMap["ews"] = newEWSCPPAlgo(); } } // AMMBench \ No newline at end of file diff --git a/src/CPPAlgos/EWSCPPAlgo.cpp b/src/CPPAlgos/EWSCPPAlgo.cpp new file mode 100644 index 00000000..8ad64467 --- /dev/null +++ b/src/CPPAlgos/EWSCPPAlgo.cpp @@ -0,0 +1,34 @@ +// +// Created by haolan on 5/29/23. +// + +#include + +namespace AMMBench { +torch::Tensor AMMBench::EWSCPPAlgo::amm(torch::Tensor A, torch::Tensor B, int k) { + auto A_size = A.sizes(); + int64_t m = A_size[0]; + int64_t n = A_size[1]; + + TORCH_CHECK(n == B.size(0)); + TORCH_CHECK(k < n); + + int64_t p = B.size(1); + + // probability distribution + torch::Tensor probs = torch::rand({m, n}); + + // S matrix that samples A with scaling + torch::Tensor mask = torch::rand_like(probs) < probs; + torch::Tensor S = torch::zeros_like(A); + S.masked_scatter_(mask, A.masked_select(mask) / probs.masked_select(mask)); + + // R matrix that samples B with scaling + torch::Tensor probs_r = torch::rand({n, p}); // a different probabilistic distribution + torch::Tensor mask_r = torch::rand_like(probs_r) < probs_r; + torch::Tensor R = torch::zeros_like(B); + R.masked_scatter_(mask_r, B.masked_select(mask_r) / probs_r.masked_select(mask_r)); + + return torch::matmul(S, R); +} +} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d5433de2..6b8a9ebb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -21,6 +21,7 @@ endmacro() add_catch_test(cpp_test SystemTest/SimpleTest.cpp IntelliStream) add_catch_test(sketch_test SystemTest/SketchTest.cpp IntelliStream) add_catch_test(crs_test SystemTest/CRSTest.cpp IntelliStream) +add_catch_test(ews_test SystemTest/EWSTest.cpp IntelliStream) add_catch_test(weighted_cr_test SystemTest/WeightedCRTest.cpp IntelliStream) add_catch_test(block_partition_test SystemTest/BlockPartitionTest.cpp IntelliStream) diff --git a/test/SystemTest/EWSTest.cpp b/test/SystemTest/EWSTest.cpp index e63cce6f..34c76cf7 100644 --- a/test/SystemTest/EWSTest.cpp +++ b/test/SystemTest/EWSTest.cpp @@ -53,4 +53,15 @@ TEST_CASE("Test the COLUMN ROW SAMPLINGS", "[short]") runSingleThreadTest("scripts/config_EWS.csv"); // place your test here REQUIRE(a == 0); +} +TEST_CASE("Test EWS in cpp", "[short]") +{ + torch::manual_seed(114514); + AMMBench::EWSCPPAlgo ews; + auto A = torch::rand({400, 400}); + auto B = torch::rand({400, 400}); + auto realC = torch::matmul(A, B); + auto ammC = ews.amm(A, B, 20); + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); + REQUIRE(froError < 0.5); } \ No newline at end of file From 3dc558e461b5dc91edb793c046992a50b2e40676 Mon Sep 17 00:00:00 2001 From: haolan Date: Tue, 30 May 2023 03:55:24 +0000 Subject: [PATCH 3/4] add Co-Occurring FD in C++ libtorch --- include/AMMBench.h | 1 + include/CPPAlgos/CoOccurringFDCPPAlgo.h | 57 +++++++++++++ src/CPPAlgos/CMakeLists.txt | 1 + src/CPPAlgos/CPPAlgoTable.cpp | 2 + src/CPPAlgos/CoOccurringFDCPPAlgo.cpp | 104 ++++++++++++++++++++++++ test/SystemTest/SketchTest.cpp | 11 +++ 6 files changed, 176 insertions(+) create mode 100644 include/CPPAlgos/CoOccurringFDCPPAlgo.h create mode 100644 src/CPPAlgos/CoOccurringFDCPPAlgo.cpp diff --git a/include/AMMBench.h b/include/AMMBench.h index 694131e7..a1201493 100755 --- a/include/AMMBench.h +++ b/include/AMMBench.h @@ -102,6 +102,7 @@ #include #include #include +#include /** * @} * diff --git a/include/CPPAlgos/CoOccurringFDCPPAlgo.h b/include/CPPAlgos/CoOccurringFDCPPAlgo.h new file mode 100644 index 00000000..6d07ac9a --- /dev/null +++ b/include/CPPAlgos/CoOccurringFDCPPAlgo.h @@ -0,0 +1,57 @@ +// +// Created by haolan on 5/29/23. +// + +#ifndef INTELLISTREAM_COOCCURRINGFDCPPALGO_H +#define INTELLISTREAM_COOCCURRINGFDCPPALGO_H +#include + +namespace AMMBench { +/** + * @ingroup AMMBENCH_CppAlgos The algorithms written in c++ + * @{ + */ +/** + * @class CPPAlgos/CoOccurringFDCPPAlgo.h + * @brief The Co-Occurring FD AMM class of c++ algos + * + */ + class CoOccurringFDCPPAlgo : public AMMBench::AbstractCPPAlgo { + public: + CoOccurringFDCPPAlgo() { + + } + + ~CoOccurringFDCPPAlgo() { + + } + + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketc or sampling + * @return the output c matrix + */ + virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, int sketchSize); + + }; + +/** + * @ingroup AMMBENCH_CppAlgos + * @typedef AbstractMatrixCppAlgoPtr + * @brief The class to describe a shared pointer to @ref CoOccurringFDCppAlgo + + */ + typedef std::shared_ptr CoOccurringFDCPPAlgoPtr; +/** + * @ingroup AMMBENCH_CppAlgos + * @def newCoOccurringFDCppAlgo + * @brief (Macro) To creat a new @ref CoOccurringFDCppAlgounder shared pointer. + */ +#define newCoOccurringFDCPPAlgo std::make_shared +} +/** + * @} + */ +#endif //INTELLISTREAM_COOCCURRINGFDCPPALGO_H diff --git a/src/CPPAlgos/CMakeLists.txt b/src/CPPAlgos/CMakeLists.txt index 5368a3c6..99e3f254 100644 --- a/src/CPPAlgos/CMakeLists.txt +++ b/src/CPPAlgos/CMakeLists.txt @@ -5,4 +5,5 @@ add_sources( CRSV2CPPAlgo.cpp BCRSCPPAlgo.cpp EWSCPPAlgo.cpp + CoOccurringFDCPPAlgo.cpp ) \ No newline at end of file diff --git a/src/CPPAlgos/CPPAlgoTable.cpp b/src/CPPAlgos/CPPAlgoTable.cpp index 99fb7f56..a6fd3689 100644 --- a/src/CPPAlgos/CPPAlgoTable.cpp +++ b/src/CPPAlgos/CPPAlgoTable.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace AMMBench { AMMBench::CPPAlgoTable::CPPAlgoTable() { algoMap["mm"] = newAbstractCPPAlgo(); @@ -14,6 +15,7 @@ AMMBench::CPPAlgoTable::CPPAlgoTable() { algoMap["crsV2"] = newCRSV2CPPAlgo(); algoMap["bcrs"] = newBCRSCPPAlgo(); algoMap["ews"] = newEWSCPPAlgo(); + algoMap["CoOFD"] = newCoOccurringFDCPPAlgo(); } } // AMMBench \ No newline at end of file diff --git a/src/CPPAlgos/CoOccurringFDCPPAlgo.cpp b/src/CPPAlgos/CoOccurringFDCPPAlgo.cpp new file mode 100644 index 00000000..5f484268 --- /dev/null +++ b/src/CPPAlgos/CoOccurringFDCPPAlgo.cpp @@ -0,0 +1,104 @@ +// +// Created by haolan on 5/29/23. +// +#include + +namespace AMMBench { +torch::Scalar get_first_element(const torch::Tensor& tensor) { + if (tensor.numel() == 1) { + return tensor.item(); + } + else { + return tensor[0].item(); + } +} + +bool is_empty_tensor(const torch::Tensor& tensor) { + return tensor.numel() == 0; +} + +torch::Tensor medianReduceRank(const torch::Tensor& SV, float delta) { + return torch::clamp(SV - delta, 0); +} + +torch::Tensor CoOccurringFDCPPAlgo::amm(const torch::Tensor A, const torch::Tensor B, int l) { + torch::Tensor B_t = B.t(); + + TORCH_CHECK(A.size(1) == B_t.size(1), "Shapes of A and B are incompatible"); + int mx = A.size(0); + int my = B_t.size(0); + int n = A.size(1); + + // Initialize sketch matrices + torch::Tensor BX = torch::zeros({ mx, l }); + torch::Tensor BY = torch::zeros({ my, l }); + + // The first l iterations + for (int i = 0; i < l; ++i) { + BX.slice(1, i, i + 1) = A.slice(1, i, i + 1); + BY.slice(1, i, i + 1) = B_t.slice(1, i, i + 1); + } + + torch::Tensor zero_columns = torch::tensor({ 0 }); + zero_columns = zero_columns.slice(0, 1); + + // Iteration l to n: insert if available, else shrink sketch matrices + for (int i = l; i < n; ++i) { + // Acquire the index of a zero-valued column + if (!is_empty_tensor(zero_columns)) { + int idx = get_first_element(zero_columns).toInt(); + BX.slice(1, idx, idx + 1) = A.slice(1, i, i + 1); + BY.slice(1, idx, idx + 1) = B_t.slice(1, i, i + 1); + zero_columns = zero_columns.slice(0, 1); + } + // If no zero-valued column, shrink accordingly + else { + torch::Tensor QX, RX; + std::tie(QX, RX) = torch::linalg_qr(BX); + torch::Tensor QY, RY; + std::tie(QY, RY) = torch::linalg_qr(BY); + torch::Tensor U, SV, V; + std::tie(U, SV, V) = torch::svd(torch::matmul(RX, RY.t())); + + // Find the median of singular values + torch::Tensor S_sorted, S_indices; + std::tie(S_sorted, S_indices) = SV.sort(); + + float delta; + if (S_sorted.size(0) % 2 == 1) { + delta = S_sorted[S_sorted.size(0) / 2].item().toFloat(); + } else { + delta = torch::median(S_sorted).item().toFloat(); + } + // Shrink the singular values with delta + torch::Tensor SV_shrunk = medianReduceRank(SV, delta); + + // Restore SV diagonal matrix + SV = torch::diag_embed(SV_shrunk); + torch::Tensor SV_sqrt = torch::sqrt(SV); + + // Update indices of zero-valued columns + torch::Tensor zero_indices = torch::nonzero(SV_shrunk == 0).squeeze(); + zero_columns = torch::cat({zero_columns, zero_indices}); + + // Convert tensor to a std::vector + std::vector vec(zero_columns.data_ptr(), zero_columns.data_ptr() + zero_columns.numel()); + + // Sort the vector + std::sort(vec.begin(), vec.end()); + + // Remove duplicates + vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); + + // Convert std::vector back to a tensor + zero_columns = torch::from_blob(vec.data(), { static_cast(vec.size()) }, torch::kInt64).clone(); + + // Update sketch matrices + BX = torch::matmul(torch::matmul(QX, U), SV_sqrt); + BY = torch::matmul(torch::matmul(QY, V), SV_sqrt); + } + } + + return torch::matmul(BX, BY.t()); +} +} \ No newline at end of file diff --git a/test/SystemTest/SketchTest.cpp b/test/SystemTest/SketchTest.cpp index 408dde15..f7fad177 100644 --- a/test/SystemTest/SketchTest.cpp +++ b/test/SystemTest/SketchTest.cpp @@ -53,4 +53,15 @@ TEST_CASE("Test the counter sketch", "[short]") runSingleThreadTest("scripts/config_counterSketch.csv"); // place your test here REQUIRE(a == 0); +} +TEST_CASE("Test Co-Occurring FD in cpp", "[short]") +{ + torch::manual_seed(114514); + AMMBench::CoOccurringFDCPPAlgo coofd; + auto A = torch::rand({400, 400}); + auto B = torch::rand({400, 400}); + auto realC = torch::matmul(A, B); + auto ammC = coofd.amm(A, B, 20); + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); + REQUIRE(froError < 0.5); } \ No newline at end of file From d7b266f70b3c2a1d69b79e43275737dd08ee413e Mon Sep 17 00:00:00 2001 From: haolan Date: Tue, 30 May 2023 14:12:58 +0000 Subject: [PATCH 4/4] add Beta-Co-Occurring FD in C++ libtorch --- include/AMMBench.h | 1 + include/CPPAlgos/BetaCoOFDCPPAlgo.h | 57 ++++++++++++++ src/CPPAlgos/BetaCoOFDCPPAlgo.cpp | 114 ++++++++++++++++++++++++++++ src/CPPAlgos/CMakeLists.txt | 1 + src/CPPAlgos/CPPAlgoTable.cpp | 2 + test/SystemTest/SketchTest.cpp | 11 +++ 6 files changed, 186 insertions(+) create mode 100644 include/CPPAlgos/BetaCoOFDCPPAlgo.h create mode 100644 src/CPPAlgos/BetaCoOFDCPPAlgo.cpp diff --git a/include/AMMBench.h b/include/AMMBench.h index a1201493..e424393c 100755 --- a/include/AMMBench.h +++ b/include/AMMBench.h @@ -103,6 +103,7 @@ #include #include #include +#include /** * @} * diff --git a/include/CPPAlgos/BetaCoOFDCPPAlgo.h b/include/CPPAlgos/BetaCoOFDCPPAlgo.h new file mode 100644 index 00000000..b89b8285 --- /dev/null +++ b/include/CPPAlgos/BetaCoOFDCPPAlgo.h @@ -0,0 +1,57 @@ +// +// Created by haolan on 5/30/23. +// + +#ifndef INTELLISTREAM_BETACOOFDCPPALGO_H +#define INTELLISTREAM_BETACOOFDCPPALGO_H +#include + +namespace AMMBench { +/** + * @ingroup AMMBENCH_CppAlgos The algorithms written in c++ + * @{ + */ +/** + * @class CPPAlgos/BetaCoOFDCPPAlgo.h + * @brief The Beta Co-Occurring FD AMM class of c++ algos + * + */ + class BetaCoOFDCPPAlgo : public AMMBench::AbstractCPPAlgo { + public: + BetaCoOFDCPPAlgo() { + + } + + ~BetaCoOFDCPPAlgo() { + + } + + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketc or sampling + * @return the output c matrix + */ + virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, int sketchSize); + + }; + +/** + * @ingroup AMMBENCH_CppAlgos + * @typedef AbstractMatrixCppAlgoPtr + * @brief The class to describe a shared pointer to @ref BetaCoOFDCppAlgo + + */ + typedef std::shared_ptr BetaCoOFDCPPAlgoPtr; +/** + * @ingroup AMMBENCH_CppAlgos + * @def newBetaCoOFDCppAlgo + * @brief (Macro) To creat a new @ref BetaCoOFDCppAlgounder shared pointer. + */ +#define newBetaCoOFDCPPAlgo std::make_shared +} +/** + * @} + */ +#endif //INTELLISTREAM_BETACOOFDCPPALGO_H diff --git a/src/CPPAlgos/BetaCoOFDCPPAlgo.cpp b/src/CPPAlgos/BetaCoOFDCPPAlgo.cpp new file mode 100644 index 00000000..f1d7d85f --- /dev/null +++ b/src/CPPAlgos/BetaCoOFDCPPAlgo.cpp @@ -0,0 +1,114 @@ +// +// Created by haolan on 5/30/23. +// +#include +torch::Scalar get_first_element(const torch::Tensor& tensor) { + if (tensor.numel() == 1) { + return tensor.item(); + } + else { + return tensor[0].item(); + } +} + +bool is_empty_tensor(const torch::Tensor& tensor) { + return tensor.numel() == 0; +} + +namespace AMMBench { +torch::Tensor attenuate(float beta, const torch::Tensor& k, int l) { + return (torch::exp(k * beta / (l - 1)) - 1) / (torch::exp(torch::tensor(beta)) - 1); +} + +torch::Tensor paramerizedReduceRank(const torch::Tensor& SV, float delta, int l, float beta) { + torch::Tensor indices = torch::arange(l, torch::kFloat32); + torch::Tensor attenuated_values = attenuate(beta, indices, l); + torch::Tensor parameterizedReduceRank = delta * attenuated_values; + torch::Tensor SV_shrunk = torch::clamp(SV - parameterizedReduceRank, 0); + return SV_shrunk; +} + +torch::Tensor BetaCoOFDCPPAlgo::amm(const torch::Tensor A, const torch::Tensor B, int l) { + torch::Tensor B_t = B.t(); + float beta = 1.0; + + TORCH_CHECK(A.size(1) == B_t.size(1), "Shapes of A and B are incompatible"); + int mx = A.size(0); + int my = B_t.size(0); + int n = A.size(1); + + // Initialize sketch matrices + torch::Tensor BX = torch::zeros({ mx, l }); + torch::Tensor BY = torch::zeros({ my, l }); + + // The first l iterations + for (int i = 0; i < l; ++i) { + BX.slice(1, i, i + 1) = A.slice(1, i, i + 1); + BY.slice(1, i, i + 1) = B_t.slice(1, i, i + 1); + } + + torch::Tensor zero_columns = torch::tensor({ 0 }); + zero_columns = zero_columns.slice(0, 1); + + // Iteration l to n: insert if available, else shrink sketch matrices + for (int i = l; i < n; ++i) { + // Acquire the index of a zero-valued column + if (!is_empty_tensor(zero_columns)) { + int idx = get_first_element(zero_columns).toInt(); + BX.slice(1, idx, idx + 1) = A.slice(1, i, i + 1); + BY.slice(1, idx, idx + 1) = B_t.slice(1, i, i + 1); + zero_columns = zero_columns.slice(0, 1); + } + // If no zero-valued column, shrink accordingly + else { + torch::Tensor QX, RX; + std::tie(QX, RX) = torch::linalg_qr(BX); + torch::Tensor QY, RY; + std::tie(QY, RY) = torch::linalg_qr(BY); + torch::Tensor U, SV, V; + std::tie(U, SV, V) = torch::svd(torch::matmul(RX, RY.t())); + + // Find the median of singular values + torch::Tensor S_sorted, S_indices; + std::tie(S_sorted, S_indices) = SV.sort(); + + float delta; + if (S_sorted.size(0) % 2 == 1) { + delta = S_sorted[S_sorted.size(0) / 2].item().toFloat(); + } else { + delta = torch::median(S_sorted).item().toFloat(); + } + // delta = S_sorted[0].item().toFloat(); + + // Shrink the singular values with delta + torch::Tensor SV_shrunk = paramerizedReduceRank(SV, delta, l, beta); + + // Restore SV diagonal matrix + SV = torch::diag_embed(SV_shrunk); + torch::Tensor SV_sqrt = torch::sqrt(SV); + + // Update indices of zero-valued columns + torch::Tensor zero_indices = torch::nonzero(SV_shrunk == 0).squeeze(); + zero_indices = zero_indices.view({ -1 }); // Convert to a 1D tensor + zero_columns = torch::cat({ zero_columns, zero_indices }); + // Convert tensor to a std::vector + std::vector vec(zero_columns.data_ptr(), zero_columns.data_ptr() + zero_columns.numel()); + + // Sort the vector + std::sort(vec.begin(), vec.end()); + + // Remove duplicates + vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); + + // Convert std::vector back to a tensor + zero_columns = torch::from_blob(vec.data(), { static_cast(vec.size()) }, torch::kInt64).clone(); + + // Update sketch matrices + BX = torch::matmul(torch::matmul(QX, U), SV_sqrt); + BY = torch::matmul(torch::matmul(QY, V), SV_sqrt); + } + } + + return torch::matmul(BX, BY.t()); +} +} \ No newline at end of file diff --git a/src/CPPAlgos/CMakeLists.txt b/src/CPPAlgos/CMakeLists.txt index 99e3f254..9d7a8934 100644 --- a/src/CPPAlgos/CMakeLists.txt +++ b/src/CPPAlgos/CMakeLists.txt @@ -6,4 +6,5 @@ add_sources( BCRSCPPAlgo.cpp EWSCPPAlgo.cpp CoOccurringFDCPPAlgo.cpp + BetaCoOFDCPPAlgo.cpp ) \ No newline at end of file diff --git a/src/CPPAlgos/CPPAlgoTable.cpp b/src/CPPAlgos/CPPAlgoTable.cpp index a6fd3689..a1472acf 100644 --- a/src/CPPAlgos/CPPAlgoTable.cpp +++ b/src/CPPAlgos/CPPAlgoTable.cpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace AMMBench { AMMBench::CPPAlgoTable::CPPAlgoTable() { algoMap["mm"] = newAbstractCPPAlgo(); @@ -16,6 +17,7 @@ AMMBench::CPPAlgoTable::CPPAlgoTable() { algoMap["bcrs"] = newBCRSCPPAlgo(); algoMap["ews"] = newEWSCPPAlgo(); algoMap["CoOFD"] = newCoOccurringFDCPPAlgo(); + algoMap["bcoofd"] = newBetaCoOFDCPPAlgo(); } } // AMMBench \ No newline at end of file diff --git a/test/SystemTest/SketchTest.cpp b/test/SystemTest/SketchTest.cpp index f7fad177..561660dd 100644 --- a/test/SystemTest/SketchTest.cpp +++ b/test/SystemTest/SketchTest.cpp @@ -64,4 +64,15 @@ TEST_CASE("Test Co-Occurring FD in cpp", "[short]") auto ammC = coofd.amm(A, B, 20); double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); REQUIRE(froError < 0.5); +} +TEST_CASE("Test Beta-Co-Occurring FD in cpp", "[short]") +{ + torch::manual_seed(114514); + AMMBench::BetaCoOFDCPPAlgo bcoofd; + auto A = torch::rand({400, 400}); + auto B = torch::rand({400, 400}); + auto realC = torch::matmul(A, B); + auto ammC = bcoofd.amm(A, B, 20); + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); + REQUIRE(froError < 0.2); } \ No newline at end of file