HEBench
original_flow_seal.cpp
Go to the documentation of this file.
1 
2 // Copyright (C) 2021 Intel Corporation
3 // SPDX-License-Identifier: Apache-2.0
4 
6 
8 #include <cassert>
9 #include <cstdint>
10 #include <iostream>
11 #include <memory>
12 #include <random>
13 #include <stdexcept>
14 
15 #include "seal/seal.h"
16 
17 class Workload
18 {
19 public:
20  Workload(std::size_t vector_size);
21 
22  std::vector<seal::Plaintext> encodeVector(const std::vector<std::vector<std::int64_t>> &vec);
23  std::vector<seal::Ciphertext> encryptVector(const std::vector<seal::Plaintext> &encoded_vec);
24  std::vector<seal::Ciphertext> eltwiseadd(const std::vector<seal::Plaintext> &A,
25  const std::vector<seal::Ciphertext> &B);
26  std::vector<seal::Plaintext> decryptResult(const std::vector<seal::Ciphertext> &encrypted_result);
27  std::vector<std::vector<int64_t>> decodeResult(const std::vector<seal::Plaintext> &encoded_result);
28 
29 private:
30  class SealBFVContext
31  {
32  public:
33  SealBFVContext(int poly_modulus_degree)
34  {
35  seal::EncryptionParameters parms{ seal::scheme_type::bfv };
36  parms.set_poly_modulus_degree(poly_modulus_degree);
37  parms.set_coeff_modulus(seal::CoeffModulus::Create(poly_modulus_degree, { 60, 60 }));
38  parms.set_plain_modulus(seal::PlainModulus::Batching(poly_modulus_degree, 20));
39  m_p_seal_context.reset(
40  new seal::SEALContext(parms, true, seal::sec_level_type::tc128));
41  m_p_keygen = std::make_unique<seal::KeyGenerator>(context());
42 
43  m_p_keygen->create_public_key(m_public_key);
44  m_secret_key = m_p_keygen->secret_key();
45 
46  m_p_encryptor = std::make_unique<seal::Encryptor>(context(), m_public_key);
47  m_p_evaluator = std::make_unique<seal::Evaluator>(context());
48  m_p_decryptor = std::make_unique<seal::Decryptor>(context(), m_secret_key);
49  m_p_batch_encoder = std::make_unique<seal::BatchEncoder>(context());
50  }
51 
52  seal::BatchEncoder &encoder() { return *m_p_batch_encoder; }
53  seal::Evaluator &evaluator() { return *m_p_evaluator; }
54  seal::Decryptor &decryptor() { return *m_p_decryptor; }
55  const seal::Encryptor &encryptor() const { return *m_p_encryptor; }
56  const seal::PublicKey &public_key() const { return m_public_key; }
57  const seal::SecretKey &secret_key() const { return m_secret_key; }
58  seal::SEALContext &context() { return *m_p_seal_context; }
59 
60  private:
61  std::shared_ptr<seal::SEALContext> m_p_seal_context;
62  std::unique_ptr<seal::KeyGenerator> m_p_keygen;
63  seal::PublicKey m_public_key;
64  seal::SecretKey m_secret_key;
65  std::unique_ptr<seal::Encryptor> m_p_encryptor;
66  std::unique_ptr<seal::Evaluator> m_p_evaluator;
67  std::unique_ptr<seal::Decryptor> m_p_decryptor;
68  std::unique_ptr<seal::BatchEncoder> m_p_batch_encoder;
69  };
70 
71  std::size_t m_vector_size;
72  std::shared_ptr<SealBFVContext> m_p_context;
73 
74  SealBFVContext &context() { return *m_p_context; }
75 };
76 
77 Workload::Workload(std::size_t vector_size)
78 {
79  if (vector_size <= 0)
80  throw std::invalid_argument("vector_size");
81  m_vector_size = vector_size;
82  m_p_context = std::make_shared<SealBFVContext>(8192);
83 }
84 
85 std::vector<seal::Plaintext> Workload::encodeVector(const std::vector<std::vector<std::int64_t>> &vec)
86 {
87  std::vector<seal::Plaintext> retval(vec.size());
88 
89  for (std::size_t i = 0; i < vec.size(); ++i)
90  {
91  assert(vec[i].size() <= context().encoder().slot_count());
92  context().encoder().encode(vec[i], retval[i]);
93  }
94  return retval;
95 }
96 
97 std::vector<seal::Ciphertext> Workload::encryptVector(const std::vector<seal::Plaintext> &encoded_vec)
98 {
99  std::vector<seal::Ciphertext> retval(encoded_vec.size());
100  for (std::size_t i = 0; i < encoded_vec.size(); i++)
101  context().encryptor().encrypt(encoded_vec[i], retval[i]);
102  return retval;
103 }
104 
105 std::vector<seal::Ciphertext> Workload::eltwiseadd(const std::vector<seal::Plaintext> &A,
106  const std::vector<seal::Ciphertext> &B)
107 {
108  std::vector<seal::Ciphertext> retval(A.size() * B.size());
109 
110  // This is the main operation function:
111  // for an offline test, it must store the result of the operation on every
112  // set of input sample in the same order as the input set.
113  // See documentation on "Ordering of Results Based on Input Batch Sizes"
114  // for details.
115  for (std::size_t A_i = 0; A_i < A.size(); ++A_i)
116  for (std::size_t B_i = 0; B_i < B.size(); ++B_i)
117  {
118  seal::Ciphertext &retval_item = retval[A_i * B.size() + B_i];
119  context().evaluator().add_plain(B[B_i], A[A_i], retval_item);
120  }
121 
122  return retval;
123 }
124 
125 std::vector<seal::Plaintext> Workload::decryptResult(const std::vector<seal::Ciphertext> &encrypted_result)
126 {
127  std::vector<seal::Plaintext> retval(encrypted_result.size());
128  for (std::size_t i = 0; i < encrypted_result.size(); i++)
129  context().decryptor().decrypt(encrypted_result[i], retval[i]);
130  return retval;
131 }
132 
133 std::vector<std::vector<int64_t>> Workload::decodeResult(const std::vector<seal::Plaintext> &encoded_result)
134 {
135  std::vector<std::vector<int64_t>> retval(encoded_result.size());
136  for (std::size_t i = 0; i < encoded_result.size(); ++i)
137  {
138  context().encoder().decode(encoded_result[i], retval[i]);
139  retval[i].resize(m_vector_size);
140  }
141  return retval;
142 }
143 
144 //---------------------------------------------------------------------
145 // main() implementation tests the workflow
146 
147 int main()
148 {
149  static const std::size_t VectorSize = 400;
150  static const std::size_t DimensionA = 2;
151  static const std::size_t DimensionB = 5;
152 
153  // START data prep
154  std::vector<std::vector<std::int64_t>> A(DimensionA, std::vector<std::int64_t>(VectorSize));
155  std::vector<std::vector<std::int64_t>> B(DimensionB, std::vector<std::int64_t>(VectorSize));
156 
157  // generate and fill vectors with random data
158  std::random_device rd;
159  std::mt19937 gen(rd());
160  std::uniform_int_distribution<> distrib(-100, 100);
161 
162  for (std::size_t i = 0; i < A.size(); ++i)
163  for (std::size_t j = 0; j < A[i].size(); ++j)
164  A[i][j] = distrib(gen);
165  for (std::size_t i = 0; i < B.size(); ++i)
166  for (std::size_t j = 0; j < B[i].size(); ++j)
167  B[i][j] = distrib(gen);
168  // END data prep
169 
170  //---------------------------------------------------------------------
171 
172  // START Backend
173  std::shared_ptr<Workload> p_workload = // initialize
174  std::make_shared<Workload>(VectorSize);
175  Workload &workload = *p_workload;
176  // For illustration purposes only: all functions in the pipeline should
177  // be able to be nested with each other in the proper order.
178  std::vector<std::vector<std::int64_t>> result =
179  workload.decodeResult(
180  workload.decryptResult(
181  workload.eltwiseadd(workload.encodeVector(A),
182  workload.encryptVector(workload.encodeVector(B)))));
183  p_workload.reset(); // terminate
184  // END Backend
185 
186  //---------------------------------------------------------------------
187 
188  // START Validation
189 
190  std::vector<std::vector<std::int64_t>> exp_out(DimensionA * DimensionB, std::vector<std::int64_t>(VectorSize));
191  assert(exp_out.size() == result.size());
192 
193  // compute ground truth
194  std::size_t result_i = 0;
195  for (std::size_t A_i = 0; A_i < A.size(); ++A_i)
196  for (std::size_t B_i = 0; B_i < B.size(); ++B_i)
197  {
198  for (std::size_t i = 0; i < VectorSize; ++i)
199  exp_out[result_i][i] = A[A_i][i] + B[B_i][i];
200  ++result_i;
201  }
202 
203  if (result == exp_out)
204  std::cout << "OK" << std::endl;
205  else
206  std::cout << "Fail" << std::endl;
207  // END Validation
208 
209  return 0;
210 }
211 
Workload
Defines all possible workloads.
Definition: types.h:83
int main(int argc, char **argv)