-
-
Notifications
You must be signed in to change notification settings - Fork 157
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement im2col and update conv2d #134
Changes from all commits
3ac1c58
df2b60c
b1d6961
16922c4
4379812
3ca630b
70c14b7
3c347a6
2503c4d
9be5a13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -564,39 +564,47 @@ CKKSVector& CKKSVector::polyval_inplace(const vector<double>& coefficients) { | |||||||||||||||||
return *this; | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
CKKSVector CKKSVector::conv2d_im2col(const vector<double>& kernel, | ||||||||||||||||||
size_t windows_nb) { | ||||||||||||||||||
CKKSVector CKKSVector::conv2d_im2col(const vector<vector<double>>& kernel, | ||||||||||||||||||
const size_t windows_nb) { | ||||||||||||||||||
CKKSVector new_vec = *this; | ||||||||||||||||||
new_vec.conv2d_im2col_inplace(kernel, windows_nb); | ||||||||||||||||||
return new_vec; | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
CKKSVector& CKKSVector::conv2d_im2col_inplace(const vector<double>& kernel, | ||||||||||||||||||
const size_t windows_nb) { | ||||||||||||||||||
vector<double> plain_vec; | ||||||||||||||||||
size_t chunks_nb = kernel.size(); | ||||||||||||||||||
|
||||||||||||||||||
CKKSVector& CKKSVector::conv2d_im2col_inplace( | ||||||||||||||||||
const vector<vector<double>>& kernel, const size_t windows_nb) { | ||||||||||||||||||
if (windows_nb == 0) { | ||||||||||||||||||
throw invalid_argument("Windows number can't be zero"); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
if (kernel.empty()) { | ||||||||||||||||||
throw invalid_argument("Kernel vector can't be empty"); | ||||||||||||||||||
if (kernel.empty() || | ||||||||||||||||||
(any_of(kernel.begin(), kernel.end(), | ||||||||||||||||||
[](const vector<double>& i) { return i.empty(); }))) { | ||||||||||||||||||
throw invalid_argument("Kernel matrix can't be empty"); | ||||||||||||||||||
Comment on lines
+580
to
+583
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about something like this? to check whether the vector is a well organized matrix?
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test will be wrong in the case where |
||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
// check if vector size is not a power of 2 | ||||||||||||||||||
if (!(chunks_nb && (!(chunks_nb & (chunks_nb - 1))))) { | ||||||||||||||||||
throw invalid_argument("Kernel size should be a power of 2"); | ||||||||||||||||||
} | ||||||||||||||||||
// flat the kernel | ||||||||||||||||||
vector<double> flatten_kernel; | ||||||||||||||||||
horizontal_scan(kernel, flatten_kernel); | ||||||||||||||||||
|
||||||||||||||||||
// calculate the next power of 2 | ||||||||||||||||||
size_t kernel_size = kernel.size() * kernel[0].size(); | ||||||||||||||||||
kernel_size = 1 << (static_cast<size_t>(ceil(log2(kernel_size)))); | ||||||||||||||||||
|
||||||||||||||||||
// pad the kernel with zeros to the next power of 2 | ||||||||||||||||||
flatten_kernel.resize(kernel_size, 0); | ||||||||||||||||||
|
||||||||||||||||||
size_t chunks_nb = flatten_kernel.size(); | ||||||||||||||||||
|
||||||||||||||||||
if (this->_size / windows_nb != chunks_nb) { | ||||||||||||||||||
throw invalid_argument("Matrix shape doesn't match with vector size"); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
vector<double> plain_vec; | ||||||||||||||||||
plain_vec.reserve(this->_size); | ||||||||||||||||||
|
||||||||||||||||||
for (size_t i = 0; i < chunks_nb; i++) { | ||||||||||||||||||
vector<double> tmp(windows_nb, kernel[i]); | ||||||||||||||||||
vector<double> tmp(windows_nb, flatten_kernel[i]); | ||||||||||||||||||
plain_vec.insert(plain_vec.end(), tmp.begin(), tmp.end()); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
#ifndef TENSEAL_UTILS_UTILS_H | ||
#define TENSEAL_UTILS_UTILS_H | ||
|
||
#include <algorithm> | ||
#include <iterator> | ||
#include <memory> | ||
|
||
#include "seal/seal.h" | ||
|
@@ -10,6 +12,107 @@ namespace tenseal { | |
using namespace seal; | ||
using namespace std; | ||
|
||
/** | ||
* horizontally scan matrix (vector of vectors) | ||
**/ | ||
template <typename T> | ||
void horizontal_scan(const vector<vector<T>>& src, vector<T>& dst) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These operations might be easier to execute using a gsl::multi_span structure. For sure not for this PR, I don't know if they actually help. But they should prevent extra-copies There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I will check this out, maybe they will be helpful. |
||
size_t in_height = src.size(); | ||
size_t in_width = src[0].size(); | ||
|
||
dst.resize(in_height * in_width); | ||
|
||
// check if each row size is equals to in_width | ||
if (any_of(src.begin(), src.end(), [in_width](const vector<T>& i) { | ||
return i.size() != in_width; | ||
})) { | ||
throw invalid_argument("rows sizes are different"); | ||
} | ||
|
||
auto start = src.begin(); | ||
auto end = src.end(); | ||
auto iter = dst.begin(); | ||
while (start != end) { | ||
iter = copy(start->begin(), start->end(), iter); | ||
start++; | ||
} | ||
} | ||
|
||
/** | ||
* vertically scan matrix (vector of vectors) | ||
**/ | ||
template <typename T> | ||
void vertical_scan(const vector<vector<T>>& src, vector<T>& dst) { | ||
size_t in_height = src.size(); | ||
size_t in_width = src[0].size(); | ||
|
||
dst.resize(in_height * in_width); | ||
|
||
// check if each row size is equals to in_width | ||
if (any_of(src.begin(), src.end(), [in_width](const vector<T>& i) { | ||
return i.size() != in_width; | ||
})) { | ||
throw invalid_argument("rows sizes are different"); | ||
} | ||
|
||
for (size_t i = 0; i < in_height; i++) { | ||
for (size_t j = 0; j < in_width; j++) { | ||
dst[i + j * in_height] = src[i][j]; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Image Block to Columns implementation | ||
**/ | ||
template <typename T> | ||
size_t im2col(const vector<vector<T>>& src, vector<vector<T>>& dst, | ||
const size_t window_height, const size_t window_width, | ||
const size_t stride) { | ||
// input shape | ||
size_t in_height = src.size(); | ||
size_t in_width = src[0].size(); | ||
|
||
if (src.empty()) { | ||
throw invalid_argument("empty matrix"); | ||
} | ||
|
||
// check if each row size is equals to in_width | ||
if (any_of(src.begin(), src.end(), [in_width](const vector<T>& i) { | ||
return i.size() != in_width; | ||
})) { | ||
throw invalid_argument("rows sizes are different"); | ||
} | ||
|
||
// output shape | ||
size_t out_height = (in_height - window_height) / stride + 1; | ||
size_t out_width = (in_width - window_width) / stride + 1; | ||
dst.reserve(out_height); | ||
|
||
// windows number | ||
size_t windows_nb = out_height * out_width; | ||
|
||
// kernel_size | ||
size_t kernel_size = window_width * window_height; | ||
// calculate the next power of 2 | ||
kernel_size = 1 << (static_cast<size_t>(ceil(log2(kernel_size)))); | ||
|
||
for (size_t j = 0; j < in_height - window_height + 1; j += stride) { | ||
for (size_t i = 0; i < in_width - window_width + 1; i += stride) { | ||
// pad the window vector to the next power of 2 of kernel size | ||
vector<T> window_vec(kernel_size, 0); | ||
auto iter = window_vec.begin(); | ||
for (size_t k = 0; k < window_height; k++) { | ||
iter = copy(src[j + k].begin() + i, | ||
src[j + k].begin() + i + window_width, iter); | ||
} | ||
dst.push_back(window_vec); | ||
} | ||
} | ||
|
||
return windows_nb; | ||
} | ||
|
||
/* | ||
Replicate the current vector as many times to fill `final_size` elements. | ||
*/ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe not in this PR, but the encoding shouldn't be doing encryption, just encoding it into a vector, then encryption should be something separate