as_waksman_components.hpp
Go to the documentation of this file.
1 //---------------------------------------------------------------------------//
2 // Copyright (c) 2018-2021 Mikhail Komarov <nemo@nil.foundation>
3 // Copyright (c) 2020-2021 Nikita Kaskov <nbering@nil.foundation>
4 //
5 // MIT License
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a copy
8 // of this software and associated documentation files (the "Software"), to deal
9 // in the Software without restriction, including without limitation the rights
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 // copies of the Software, and to permit persons to whom the Software is
12 // furnished to do so, subject to the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included in all
15 // copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 // SOFTWARE.
24 //---------------------------------------------------------------------------//
25 // @file Declaration of interfaces for the AS-Waksman routing component.
26 //
27 // The component verifies that the outputs are a permutation of the inputs,
28 // by use of an AS-Waksman network.
29 //---------------------------------------------------------------------------//
30 
31 #ifndef CRYPTO3_ZK_BLUEPRINT_AS_WAKSMAN_ROUTING_COMPONENT_HPP
32 #define CRYPTO3_ZK_BLUEPRINT_AS_WAKSMAN_ROUTING_COMPONENT_HPP
33 
36 
39 
40 namespace nil {
41  namespace crypto3 {
42  namespace zk {
43  namespace components {
44 
45  template<typename FieldType>
46  struct as_waksman_routing_component : public component<FieldType> {
47 
48  /*
49  Indexing conventions:
50 
51  routed_packets[column_idx][packet_idx][subpacket_idx]
52  pack_inputs/unpack_outputs[packet_idx]
53  asw_switch_bits[column_idx][row_idx]
54 
55  Where column_idx ranges is in range 0 .. width and packet_idx is
56  in range 0 .. num_packets-1.
57 
58  Note that unlike in Bene\v{s} routing networks row_idx are
59  *not* necessarily consecutive; similarly for straight edges
60  routed_packets[column_idx][packet_idx] will *reuse* previously
61  allocated variables.
62 
63  */
64  std::vector<std::vector<blueprint_variable_vector<FieldType>>> routed_packets;
65  std::vector<multipacking_component<FieldType>> pack_inputs, unpack_outputs;
66 
67  /*
68  If #packets = 1 then we can route without explicit switch bits
69  (and save half the constraints); in this case asw_switch_bits will
70  be unused.
71 
72  For asw_switch_bits 0 corresponds to switch off (straight
73  connection), and 1 corresponds to switch on (crossed
74  connection).
75  */
76  std::vector<std::map<std::size_t, blueprint_variable<FieldType>>> asw_switch_bits;
78 
79  public:
80  const std::size_t num_packets;
81  const std::size_t num_columns;
82  const std::vector<blueprint_variable_vector<FieldType>> routing_input_bits;
83  const std::vector<blueprint_variable_vector<FieldType>> routing_output_bits;
84 
85  const std::size_t packet_size, num_subpackets;
86 
89  const std::size_t num_packets,
93  void generate_r1cs_witness(const integer_permutation &permutation);
94  };
95 
96  template<typename FieldType>
97  void test_as_waksman_routing_component(const std::size_t num_packets, const std::size_t packet_size);
98 
99  template<typename FieldType>
102  const std::size_t num_packets,
103  const std::vector<blueprint_variable_vector<FieldType>> &routing_input_bits,
104  const std::vector<blueprint_variable_vector<FieldType>> &routing_output_bits) :
105  component<FieldType>(bp),
106  num_packets(num_packets), num_columns(as_waksman_num_columns(num_packets)),
107  routing_input_bits(routing_input_bits), routing_output_bits(routing_output_bits),
108  packet_size(routing_input_bits[0].size()),
109  num_subpackets((packet_size + FieldType::capacity() - 1) / FieldType::capacity()) {
111  routed_packets.resize(num_columns + 1);
112 
113  /* Two pass allocation. First allocate LHS packets, then for every
114  switch either copy over the variables from previously allocated
115  to allocate target packets */
116  routed_packets[0].resize(num_packets);
117  for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) {
118  routed_packets[0][packet_idx].allocate(bp, num_subpackets);
119  }
120 
121  for (std::size_t column_idx = 0; column_idx < num_columns; ++column_idx) {
122  routed_packets[column_idx + 1].resize(num_packets);
123 
124  for (std::size_t row_idx = 0; row_idx < num_packets; ++row_idx) {
125  if (neighbors[column_idx][row_idx].first == neighbors[column_idx][row_idx].second) {
126  /* This is a straight edge, so just copy over the previously allocated subpackets */
127  routed_packets[column_idx + 1][neighbors[column_idx][row_idx].first] =
128  routed_packets[column_idx][row_idx];
129  } else {
130  const std::size_t straight_edge = neighbors[column_idx][row_idx].first;
131  const std::size_t cross_edge = neighbors[column_idx][row_idx].second;
132  routed_packets[column_idx + 1][straight_edge].allocate(bp, num_subpackets);
133  routed_packets[column_idx + 1][cross_edge].allocate(bp, num_subpackets);
134  ++row_idx; /* skip the next idx, as it to refers to the same packets */
135  }
136  }
137  }
138 
139  /* create packing/unpacking components */
140  pack_inputs.reserve(num_packets);
141  unpack_outputs.reserve(num_packets);
142  for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) {
144  bp,
146  routing_input_bits[packet_idx].end()),
147  routed_packets[0][packet_idx],
148  FieldType::capacity()));
150  bp,
152  routing_output_bits[packet_idx].end()),
153  routed_packets[num_columns][packet_idx],
154  FieldType::capacity()));
155  }
156 
157  /* allocate switch bits */
158  if (num_subpackets > 1) {
160 
161  for (std::size_t column_idx = 0; column_idx < num_columns; ++column_idx) {
162  for (std::size_t row_idx = 0; row_idx < num_packets; ++row_idx) {
163  if (neighbors[column_idx][row_idx].first != neighbors[column_idx][row_idx].second) {
164  asw_switch_bits[column_idx][row_idx].allocate(bp);
165  ++row_idx; /* next row_idx corresponds to the same switch, so skip it */
166  }
167  }
168  }
169  }
170  }
171 
172  template<typename FieldType>
174  /* packing/unpacking */
175  for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) {
176  pack_inputs[packet_idx].generate_r1cs_constraints(false);
177  unpack_outputs[packet_idx].generate_r1cs_constraints(true);
178  }
179 
180  /* actual routing constraints */
181  for (std::size_t column_idx = 0; column_idx < num_columns; ++column_idx) {
182  for (std::size_t row_idx = 0; row_idx < num_packets; ++row_idx) {
183  if (neighbors[column_idx][row_idx].first == neighbors[column_idx][row_idx].second) {
184  /* if there is no switch at this position, then just continue with next row_idx */
185  continue;
186  }
187 
188  if (num_subpackets == 1) {
189  /* easy case: require that
190  (cur-straight_edge)*(cur-cross_edge) = 0 for both
191  switch inputs */
192  for (std::size_t switch_input : {row_idx, row_idx + 1}) {
193  const std::size_t straight_edge = neighbors[column_idx][switch_input].first;
194  const std::size_t cross_edge = neighbors[column_idx][switch_input].second;
195 
196  this->bp.add_r1cs_constraint(snark::r1cs_constraint<FieldType>(
197  routed_packets[column_idx][switch_input][0] -
198  routed_packets[column_idx + 1][straight_edge][0],
199  routed_packets[column_idx][switch_input][0] -
200  routed_packets[column_idx + 1][cross_edge][0],
201  0));
202  }
203  } else {
204  /* require switching bit to be boolean */
205  generate_boolean_r1cs_constraint<FieldType>(this->bp,
206  asw_switch_bits[column_idx][row_idx]);
207 
208  /* route forward according to the switch bit */
209  for (std::size_t subpacket_idx = 0; subpacket_idx < num_subpackets; ++subpacket_idx) {
210  /*
211  (1-switch_bit) * (cur-straight_edge) + switch_bit * (cur-cross_edge) = 0
212  switch_bit * (cross_edge-straight_edge) = cur-straight_edge
213  */
214  for (std::size_t switch_input : {row_idx, row_idx + 1}) {
215  const std::size_t straight_edge = neighbors[column_idx][switch_input].first;
216  const std::size_t cross_edge = neighbors[column_idx][switch_input].second;
217 
218  this->bp.add_r1cs_constraint(snark::r1cs_constraint<FieldType>(
219  asw_switch_bits[column_idx][row_idx],
220  routed_packets[column_idx + 1][cross_edge][subpacket_idx] -
221  routed_packets[column_idx + 1][straight_edge][subpacket_idx],
222  routed_packets[column_idx][switch_input][subpacket_idx] -
223  routed_packets[column_idx + 1][straight_edge][subpacket_idx]));
224  }
225  }
226  }
227 
228  /* we processed both switch inputs at once, so skip the next iteration */
229  ++row_idx;
230  }
231  }
232  }
233 
234  template<typename FieldType>
236  const integer_permutation &permutation) {
237  /* pack inputs */
238  for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) {
239  pack_inputs[packet_idx].generate_r1cs_witness_from_bits();
240  }
241 
242  /* do the routing */
243  as_waksman_routing routing = get_as_waksman_routing(permutation);
244 
245  for (std::size_t column_idx = 0; column_idx < num_columns; ++column_idx) {
246  for (std::size_t row_idx = 0; row_idx < num_packets; ++row_idx) {
247  if (neighbors[column_idx][row_idx].first == neighbors[column_idx][row_idx].second) {
248  /* this is a straight edge, so just pass the values forward */
249  const std::size_t next = neighbors[column_idx][row_idx].first;
250 
251  for (std::size_t subpacket_idx = 0; subpacket_idx < num_subpackets; ++subpacket_idx) {
252  this->bp.val(routed_packets[column_idx + 1][next][subpacket_idx]) =
253  this->bp.val(routed_packets[column_idx][row_idx][subpacket_idx]);
254  }
255  } else {
256  if (num_subpackets > 1) {
257  /* update the switch bit */
258  this->bp.val(asw_switch_bits[column_idx][row_idx]) =
259  typename FieldType::value_type(routing[column_idx][row_idx] ? 1 : 0);
260  }
261 
262  /* route according to the switch bit */
263  const bool switch_val = routing[column_idx][row_idx];
264 
265  for (std::size_t switch_input : {row_idx, row_idx + 1}) {
266  const std::size_t straight_edge = neighbors[column_idx][switch_input].first;
267  const std::size_t cross_edge = neighbors[column_idx][switch_input].second;
268 
269  const std::size_t switched_edge = (switch_val ? cross_edge : straight_edge);
270 
271  for (std::size_t subpacket_idx = 0; subpacket_idx < num_subpackets;
272  ++subpacket_idx) {
273  this->bp.val(routed_packets[column_idx + 1][switched_edge][subpacket_idx]) =
274  this->bp.val(routed_packets[column_idx][switch_input][subpacket_idx]);
275  }
276  }
277 
278  /* we processed both switch inputs at once, so skip the next iteration */
279  ++row_idx;
280  }
281  }
282  }
283 
284  /* unpack outputs */
285  for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) {
286  unpack_outputs[packet_idx].generate_r1cs_witness_from_packed();
287  }
288  }
289 
290  template<typename FieldType>
291  void test_as_waksman_routing_component(const std::size_t num_packets, const std::size_t packet_size) {
293  integer_permutation permutation(num_packets);
294  permutation.random_shuffle();
295 
296  std::vector<blueprint_variable_vector<FieldType>> randbits(num_packets), outbits(num_packets);
297  for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) {
298  randbits[packet_idx].allocate(bp, packet_size);
299  outbits[packet_idx].allocate(bp, packet_size);
300 
301  for (std::size_t bit_idx = 0; bit_idx < packet_size; ++bit_idx) {
302  bp.val(randbits[packet_idx][bit_idx]) =
303  (rand() % 2) ? FieldType::value_type::zero() : FieldType::value_type::zero();
304  }
305  }
306  as_waksman_routing_component<FieldType> r(bp, num_packets, randbits, outbits);
308 
309  r.generate_r1cs_witness(permutation);
310 
311  assert(bp.is_satisfied());
312  for (std::size_t packet_idx = 0; packet_idx < num_packets; ++packet_idx) {
313  for (std::size_t bit_idx = 0; bit_idx < packet_size; ++bit_idx) {
314  assert(bp.val(outbits[permutation.get(packet_idx)][bit_idx]) ==
315  bp.val(randbits[packet_idx][bit_idx]));
316  }
317  }
318 
319  bp.val(blueprint_variable<FieldType>(10)) = typename FieldType::value_type(12345);
320  assert(!bp.is_satisfied());
321  }
322 
323  } // namespace components
324  } // namespace zk
325  } // namespace crypto3
326 } // namespace nil
327 
328 #endif // CRYPTO3_ZK_BLUEPRINT_AS_WAKSMAN_ROUTING_COMPONENT_HPP
Definition: blueprint_variable.hpp:57
Definition: blueprint_variable.hpp:46
Definition: blueprint.hpp:46
FieldType::value_type & val(const blueprint_variable< FieldType > &var)
Definition: blueprint.hpp:70
bool is_satisfied() const
Definition: blueprint.hpp:102
Definition: component.hpp:37
blueprint< FieldType > & bp
Definition: component.hpp:39
vector(T, U...) -> vector< std::enable_if_t<(std::is_same_v< T, U > &&...), T >, 1+sizeof...(U)>
deduction guide for uniform initialization
void test_as_waksman_routing_component(const std::size_t num_packets, const std::size_t packet_size)
Definition: as_waksman_components.hpp:291
std::size_t as_waksman_num_columns(size_t num_packets)
Definition: as_waksman.hpp:186
std::vector< std::vector< std::pair< std::size_t, std::size_t > > > as_waksman_topology
Definition: as_waksman.hpp:99
std::vector< std::map< std::size_t, bool > > as_waksman_routing
Definition: as_waksman.hpp:118
as_waksman_topology generate_as_waksman_topology(size_t num_packets)
Definition: as_waksman.hpp:299
as_waksman_routing get_as_waksman_routing(const integer_permutation &permutation)
Definition: as_waksman.hpp:612
Definition: pair.hpp:31
Definition: as_waksman_components.hpp:46
std::vector< multipacking_component< FieldType > > unpack_outputs
Definition: as_waksman_components.hpp:65
const std::size_t packet_size
Definition: as_waksman_components.hpp:85
const std::vector< blueprint_variable_vector< FieldType > > routing_output_bits
Definition: as_waksman_components.hpp:83
const std::size_t num_packets
Definition: as_waksman_components.hpp:80
std::vector< multipacking_component< FieldType > > pack_inputs
Definition: as_waksman_components.hpp:65
const std::size_t num_columns
Definition: as_waksman_components.hpp:81
void generate_r1cs_constraints()
Definition: as_waksman_components.hpp:173
const std::size_t num_subpackets
Definition: as_waksman_components.hpp:85
std::vector< std::map< std::size_t, blueprint_variable< FieldType > > > asw_switch_bits
Definition: as_waksman_components.hpp:76
as_waksman_routing_component(blueprint< FieldType > &bp, const std::size_t num_packets, const std::vector< blueprint_variable_vector< FieldType >> &routing_input_bits, const std::vector< blueprint_variable_vector< FieldType >> &routing_output_bits)
Definition: as_waksman_components.hpp:100
void generate_r1cs_witness(const integer_permutation &permutation)
Definition: as_waksman_components.hpp:235
const std::vector< blueprint_variable_vector< FieldType > > routing_input_bits
Definition: as_waksman_components.hpp:82
std::vector< std::vector< blueprint_variable_vector< FieldType > > > routed_packets
Definition: as_waksman_components.hpp:64
as_waksman_topology neighbors
Definition: as_waksman_components.hpp:77