Clearmatics Libsnark  0.1
C++ library for zkSNARK proofs
sparse_vector.tcc
Go to the documentation of this file.
1 /** @file
2  *****************************************************************************
3 
4  Implementation of interfaces for a sparse vector.
5 
6  See sparse_vector.hpp .
7 
8  *****************************************************************************
9  * @author This file is part of libsnark, developed by SCIPR Lab
10  * and contributors (see AUTHORS).
11  * @copyright MIT license (see LICENSE file)
12  *****************************************************************************/
13 
14 #ifndef SPARSE_VECTOR_TCC_
15 #define SPARSE_VECTOR_TCC_
16 
17 #include <numeric>
18 
19 #ifdef MULTICORE
20 #include <omp.h>
21 #endif
22 
23 #include <libff/algebra/scalar_multiplication/multiexp.hpp>
24 
25 namespace libsnark
26 {
27 
28 template<typename T>
29 sparse_vector<T>::sparse_vector(std::vector<T> &&v)
30  : values(std::move(v)), domain_size_(values.size())
31 {
32  indices.resize(domain_size_);
33  std::iota(indices.begin(), indices.end(), 0);
34 }
35 
36 template<typename T> T sparse_vector<T>::operator[](const size_t idx) const
37 {
38  auto it = std::lower_bound(indices.begin(), indices.end(), idx);
39  return (it != indices.end() && *it == idx) ? values[it - indices.begin()]
40  : T();
41 }
42 
43 template<typename T>
44 bool sparse_vector<T>::operator==(const sparse_vector<T> &other) const
45 {
46  if (this->domain_size_ != other.domain_size_) {
47  return false;
48  }
49 
50  size_t this_pos = 0, other_pos = 0;
51  while (this_pos < this->indices.size() &&
52  other_pos < other.indices.size()) {
53  if (this->indices[this_pos] == other.indices[other_pos]) {
54  if (this->values[this_pos] != other.values[other_pos]) {
55  return false;
56  }
57  ++this_pos;
58  ++other_pos;
59  } else if (this->indices[this_pos] < other.indices[other_pos]) {
60  if (!this->values[this_pos].is_zero()) {
61  return false;
62  }
63  ++this_pos;
64  } else {
65  if (!other.values[other_pos].is_zero()) {
66  return false;
67  }
68  ++other_pos;
69  }
70  }
71 
72  /* at least one of the vectors has been exhausted, so other must be empty */
73  while (this_pos < this->indices.size()) {
74  if (!this->values[this_pos].is_zero()) {
75  return false;
76  }
77  ++this_pos;
78  }
79 
80  while (other_pos < other.indices.size()) {
81  if (!other.values[other_pos].is_zero()) {
82  return false;
83  }
84  ++other_pos;
85  }
86 
87  return true;
88 }
89 
90 template<typename T>
91 bool sparse_vector<T>::operator==(const std::vector<T> &other) const
92 {
93  if (this->domain_size_ < other.size()) {
94  return false;
95  }
96 
97  size_t j = 0;
98  for (size_t i = 0; i < other.size(); ++i) {
99  if (this->indices[j] == i) {
100  if (this->values[j] != other[j]) {
101  return false;
102  }
103  ++j;
104  } else {
105  if (!other[j].is_zero()) {
106  return false;
107  }
108  }
109  }
110 
111  return true;
112 }
113 
114 template<typename T> bool sparse_vector<T>::is_valid() const
115 {
116  if (values.size() == indices.size() && values.size() <= domain_size_) {
117  return false;
118  }
119 
120  for (size_t i = 0; i + 1 < indices.size(); ++i) {
121  if (indices[i] >= indices[i + 1]) {
122  return false;
123  }
124  }
125 
126  if (!indices.empty() && indices[indices.size() - 1] >= domain_size_) {
127  return false;
128  }
129 
130  return true;
131 }
132 
133 template<typename T> bool sparse_vector<T>::empty() const
134 {
135  return indices.empty();
136 }
137 
138 template<typename T> size_t sparse_vector<T>::domain_size() const
139 {
140  return domain_size_;
141 }
142 
143 template<typename T> size_t sparse_vector<T>::size() const
144 {
145  return indices.size();
146 }
147 
148 template<typename T> size_t sparse_vector<T>::size_in_bits() const
149 {
150  return indices.size() * (sizeof(size_t) * 8 + T::size_in_bits());
151 }
152 
153 template<typename T>
154 template<typename FieldT>
155 std::pair<T, sparse_vector<T>> sparse_vector<T>::accumulate(
156  const typename std::vector<FieldT>::const_iterator &it_begin,
157  const typename std::vector<FieldT>::const_iterator &it_end,
158  const size_t offset) const
159 {
160 #ifdef MULTICORE
161  // to override, set OMP_NUM_THREADS env var or call
162  // omp_set_num_threads()
163  const size_t chunks = omp_get_max_threads();
164 #else
165  const size_t chunks = 1;
166 #endif
167 
168  T accumulated_value = T::zero();
169  sparse_vector<T> resulting_vector;
170  resulting_vector.domain_size_ = domain_size_;
171 
172  const size_t range_len = it_end - it_begin;
173  bool in_block = false;
174 
175  // g++ -flto emits unitialized warning, even though in_block
176  // guards for such cases.
177  size_t first_pos = -1;
178  size_t last_pos = -1;
179  for (size_t i = 0; i < indices.size(); ++i) {
180  const bool matching_pos =
181  (offset <= indices[i] && indices[i] < offset + range_len);
182  // printf("i = %zu, pos[i] = %zu, offset = %zu, w_size = %zu\n", i,
183  // indices[i], offset, w_size);
184  bool copy_over;
185 
186  if (in_block) {
187  if (matching_pos && last_pos == i - 1) {
188  // block can be extended, do it
189  last_pos = i;
190  copy_over = false;
191  } else {
192  // block has ended here
193  in_block = false;
194  copy_over = true;
195 
196 #ifdef DEBUG
197  libff::print_indent();
198  printf(
199  "doing multiexp for w_%zu ... w_%zu\n",
200  indices[first_pos],
201  indices[last_pos]);
202 #endif
203  accumulated_value =
204  accumulated_value +
205  libff::multi_exp<
206  T,
207  FieldT,
208  libff::multi_exp_method_bos_coster>(
209  values.begin() + first_pos,
210  values.begin() + last_pos + 1,
211  it_begin + (indices[first_pos] - offset),
212  it_begin + (indices[last_pos] - offset) + 1,
213  chunks);
214  }
215  } else {
216  if (matching_pos) {
217  // block can be started
218  first_pos = i;
219  last_pos = i;
220  in_block = true;
221  copy_over = false;
222  } else {
223  copy_over = true;
224  }
225  }
226 
227  if (copy_over) {
228  resulting_vector.indices.emplace_back(indices[i]);
229  resulting_vector.values.emplace_back(values[i]);
230  }
231  }
232 
233  if (in_block) {
234 #ifdef DEBUG
235  libff::print_indent();
236  printf(
237  "doing multiexp for w_%zu ... w_%zu\n",
238  indices[first_pos],
239  indices[last_pos]);
240 #endif
241  accumulated_value =
242  accumulated_value +
243  libff::multi_exp<T, FieldT, libff::multi_exp_method_bos_coster>(
244  values.begin() + first_pos,
245  values.begin() + last_pos + 1,
246  it_begin + (indices[first_pos] - offset),
247  it_begin + (indices[last_pos] - offset) + 1,
248  chunks);
249  }
250 
251  return std::make_pair(accumulated_value, resulting_vector);
252 }
253 
254 template<typename T>
255 std::ostream &operator<<(std::ostream &out, const sparse_vector<T> &v)
256 {
257  out << v.domain_size_ << "\n";
258  out << v.indices.size() << "\n";
259  for (const size_t &i : v.indices) {
260  out << i << "\n";
261  }
262 
263  out << v.values.size() << "\n";
264  for (const T &t : v.values) {
265  out << t << OUTPUT_NEWLINE;
266  }
267 
268  return out;
269 }
270 
271 template<typename T>
272 std::istream &operator>>(std::istream &in, sparse_vector<T> &v)
273 {
274  in >> v.domain_size_;
275  libff::consume_newline(in);
276 
277  size_t s;
278  in >> s;
279  libff::consume_newline(in);
280  v.indices.resize(s);
281  for (size_t i = 0; i < s; ++i) {
282  in >> v.indices[i];
283  libff::consume_newline(in);
284  }
285 
286  v.values.clear();
287  in >> s;
288  libff::consume_newline(in);
289  v.values.reserve(s);
290 
291  for (size_t i = 0; i < s; ++i) {
292  T t;
293  in >> t;
294  libff::consume_OUTPUT_NEWLINE(in);
295  v.values.emplace_back(t);
296  }
297 
298  return in;
299 }
300 
301 } // namespace libsnark
302 
303 #endif // SPARSE_VECTOR_TCC_