improved_parallel_fhe.rs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::time::Instant;
  19. use rayon::prelude::*;
  20. use tfhe::integer::ciphertext::RadixCiphertext;
  21. use tfhe::integer::{IntegerCiphertext, ServerKey};
  22. use crate::NUMBER_OF_BLOCKS;
  23. fn compute_prefix_sum(server_key: &ServerKey, arr: &[RadixCiphertext]) -> Vec<RadixCiphertext> {
  24. if arr.is_empty() {
  25. return arr.to_vec();
  26. }
  27. let mut prefix_sum: Vec<RadixCiphertext> = (0..arr.len().next_power_of_two())
  28. .into_par_iter()
  29. .map(|i| {
  30. if i < arr.len() {
  31. arr[i].clone()
  32. } else {
  33. server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS)
  34. }
  35. })
  36. .collect();
  37. for d in 0..prefix_sum.len().ilog2() {
  38. prefix_sum
  39. .par_chunks_exact_mut(2_usize.pow(d + 1))
  40. .for_each(move |chunk| {
  41. let length = chunk.len();
  42. let mut left = chunk.get((length - 1) / 2).unwrap().clone();
  43. server_key.smart_add_assign_parallelized(chunk.last_mut().unwrap(), &mut left)
  44. });
  45. }
  46. let last = prefix_sum.last().unwrap().clone();
  47. *prefix_sum.last_mut().unwrap() = server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS);
  48. for d in (0..prefix_sum.len().ilog2()).rev() {
  49. prefix_sum
  50. .par_chunks_exact_mut(2_usize.pow(d + 1))
  51. .for_each(move |chunk| {
  52. let length = chunk.len();
  53. let temp = chunk.last().unwrap().clone();
  54. let mut mid = chunk.get((length - 1) / 2).unwrap().clone();
  55. server_key.smart_add_assign_parallelized(chunk.last_mut().unwrap(), &mut mid);
  56. chunk[(length - 1) / 2] = temp;
  57. });
  58. }
  59. prefix_sum.push(last);
  60. prefix_sum[1..=arr.len()].to_vec()
  61. }
  62. fn fill_orders(
  63. server_key: &ServerKey,
  64. total_orders: &RadixCiphertext,
  65. orders: &mut [RadixCiphertext],
  66. prefix_sum_arr: &[RadixCiphertext],
  67. ) {
  68. orders
  69. .into_par_iter()
  70. .enumerate()
  71. .for_each(move |(i, order)| {
  72. // (total_orders - previous_prefix_sum).max(0)
  73. let mut diff = if i == 0 {
  74. total_orders.clone()
  75. } else {
  76. let previous_prefix_sum = &prefix_sum_arr[i - 1];
  77. // total_orders - previous_prefix_sum
  78. let mut diff = server_key.smart_sub_parallelized(
  79. &mut total_orders.clone(),
  80. &mut previous_prefix_sum.clone(),
  81. );
  82. // total_orders > prefix_sum
  83. let mut cond = server_key
  84. .smart_gt_parallelized(
  85. &mut total_orders.clone(),
  86. &mut previous_prefix_sum.clone(),
  87. )
  88. .into_radix(diff.blocks().len(), server_key);
  89. // (total_orders - previous_prefix_sum) * (total_orders > previous_prefix_sum)
  90. // = (total_orders - previous_prefix_sum).max(0)
  91. server_key.smart_mul_parallelized(&mut cond, &mut diff)
  92. };
  93. // (total_orders - previous_prefix_sum).max(0).min(*order);
  94. *order = server_key.smart_min_parallelized(&mut diff, order);
  95. });
  96. }
  97. /// FHE implementation of the volume matching algorithm.
  98. ///
  99. /// In this function, the implemented algorithm is modified to utilize more concurrency.
  100. ///
  101. /// Matches the given encrypted [sell_orders] with encrypted [buy_orders] using the given
  102. /// [server_key]. The amount of the orders that are successfully filled is written over the original
  103. /// order count.
  104. pub fn volume_match(
  105. sell_orders: &mut [RadixCiphertext],
  106. buy_orders: &mut [RadixCiphertext],
  107. server_key: &ServerKey,
  108. ) {
  109. println!("Creating prefix sum arrays...");
  110. let time = Instant::now();
  111. let (prefix_sum_sell_orders, prefix_sum_buy_orders) = rayon::join(
  112. || compute_prefix_sum(server_key, sell_orders),
  113. || compute_prefix_sum(server_key, buy_orders),
  114. );
  115. println!("Created prefix sum arrays in {:?}", time.elapsed());
  116. let zero = server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS);
  117. let total_buy_orders = prefix_sum_buy_orders.last().unwrap_or(&zero);
  118. let total_sell_orders = prefix_sum_sell_orders.last().unwrap_or(&zero);
  119. println!("Matching orders...");
  120. let time = Instant::now();
  121. rayon::join(
  122. || {
  123. fill_orders(
  124. server_key,
  125. total_sell_orders,
  126. buy_orders,
  127. &prefix_sum_buy_orders,
  128. )
  129. },
  130. || {
  131. fill_orders(
  132. server_key,
  133. total_buy_orders,
  134. sell_orders,
  135. &prefix_sum_sell_orders,
  136. )
  137. },
  138. );
  139. println!("Matched orders in {:?}", time.elapsed());
  140. }