Skip to content

Commit

Permalink
multithreaded version
Browse files Browse the repository at this point in the history
  • Loading branch information
Optalysys-Lab committed Oct 27, 2021
1 parent 8f01065 commit 8b11743
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 56 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ edition = "2018"

[dependencies]
concrete-boolean = "0.1.0"
ggez = "0.6.0"
glam = "0.15.2"
nalgebra = "0.18"
rayon = "1.5"
8 changes: 8 additions & 0 deletions Config.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
500 40 80 80 0. 0. 0. 1. 1. 1. 1 10000
# The first line of this file contains the configuration data. Each number must be followed by a space.
# In order, they represent:
# the time between steps in µs
# the pixel size
# the grid dimensions (height then depth)
# the ‘dead’ color (r, g, b)
# the ‘alive’ color (r, g, b)
6 changes: 6 additions & 0 deletions initial_state.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
1 0 0 0 0 0
0 1 1 0 0 0
1 1 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
73 changes: 73 additions & 0 deletions src/graph.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
pub use ggez::{ GameResult, Context, graphics, conf, event };
use std::time::Duration;
use std::thread::sleep;
use std::sync::Mutex;
use super::*;

pub struct MainState {
board: Board,
time_step: Duration,
first_frame: bool,
pixel_size: usize,
col1: (f32,f32,f32),
col2: (f32,f32,f32),
server_keys: Vec<Mutex<ServerKey>>,
zeros: (Ciphertext, Ciphertext, Ciphertext),
client_key: ClientKey
}

impl MainState {
pub fn new(board: Board, config: &Config,
server_keys: Vec<Mutex<ServerKey>>, zeros: (Ciphertext, Ciphertext, Ciphertext),
client_key: ClientKey)
-> Result<MainState, Box<dyn std::error::Error>>
{
Ok(MainState { board,
time_step: Duration::from_micros(config.wait_time_micros),
first_frame: true,
pixel_size: config.pixel_size,
col1: config.col1,
col2: config.col2,
server_keys,
zeros,
client_key })
}
}

impl event::EventHandler<ggez::GameError> for MainState {

fn update(&mut self, _ctx: &mut Context) -> GameResult {
if self.first_frame {
self.first_frame = false;
} else {
sleep(self.time_step);
self.board.update(&self.server_keys, &self.zeros);
}
Ok(())
}

fn draw(&mut self, mut ctx: &mut Context) -> GameResult {

// clear the window
graphics::clear(ctx, [self.col1.0, self.col1.1, self.col1.2, 1.].into());

for i in 0..self.board.dimensions.0 {
for j in 0..self.board.dimensions.1 {
if self.client_key.decrypt(&self.board.states[i*self.board.dimensions.1 + j]) {
let pixel = graphics::MeshBuilder::new().rectangle(
graphics::DrawMode::Fill(graphics::FillOptions::DEFAULT),
graphics::Rect::new(0., 0., self.pixel_size as f32, self.pixel_size as f32),
graphics::Color::new(self.col2.0, self.col2.1, self.col2.2, 1.)
)
.unwrap()
.build(&mut ctx).unwrap();
graphics::draw(ctx, &pixel, graphics::DrawParam::new()
.offset([-((j*self.pixel_size) as f32), -((i*self.pixel_size) as f32)]))?;
}
}
}

graphics::present(ctx)?;
Ok(())
}
}
136 changes: 112 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
pub use concrete_boolean::{ gen_keys, server_key::ServerKey, ciphertext::Ciphertext };
pub use concrete_boolean::{ gen_keys, server_key::ServerKey, ciphertext::Ciphertext,
client_key::ClientKey };
use rayon::prelude::*;

mod graph;
pub use graph::*;

struct SendPtr (*const ServerKey);
unsafe impl Sync for SendPtr {}
unsafe impl Send for SendPtr {}

// add one encrypted bit `a` to the encrypted binary representation `b` of a 3-bit number, with 8
// identified with 0
Expand Down Expand Up @@ -122,7 +131,7 @@ impl Board {
/// let mut board = Board::new(n_cols, states);
///
/// // update the board
/// board.evolve(&server_key, &zeros);
/// board.update(&server_key, &zeros);
///
/// // decrypt and show the board
/// for i in 0..n_rows {
Expand All @@ -137,37 +146,116 @@ impl Board {
/// }
/// println!("");
/// ```
pub fn evolve(&mut self, server_key: &ServerKey, zeros: &(Ciphertext, Ciphertext, Ciphertext)) {
pub fn update(&mut self, server_keys: &Vec<std::sync::Mutex<ServerKey>>, zeros: &(Ciphertext, Ciphertext, Ciphertext))
{

let mut new_states = Vec::<Ciphertext>::new();

let nx = self.dimensions.0;
let ny = self.dimensions.1;
for i in 0..nx {

let new_states = (0..nx*ny).into_par_iter().map( |k| {

let i = k / ny;
let j = k % ny;

let im = if i == 0 { nx-1 } else { i-1 };
let ip = if i == nx-1 { 0 } else { i+1 };
for j in 0..ny {
let jm = if j == 0 { ny-1 } else { j-1 };
let jp = if j == ny-1 { 0 } else { j+1 };

// get the neighbours, with periodic boundary conditions
let n1 = &self.states[im*ny+jm];
let n2 = &self.states[im*ny+j];
let n3 = &self.states[im*ny+jp];
let n4 = &self.states[i*ny+jm];
let n5 = &self.states[i*ny+jp];
let n6 = &self.states[ip*ny+jm];
let n7 = &self.states[ip*ny+j];
let n8 = &self.states[ip*ny+jp];

// see if the cell is alive of dead
new_states.push(is_alive(server_key, &self.states[i*ny+j],
&vec![n1,n2,n3,n4,n5,n6,n7,n8], zeros));
let jm = if j == 0 { ny-1 } else { j-1 };
let jp = if j == ny-1 { 0 } else { j+1 };

// get the neighbours, with periodic boundary conditions
let n1 = &self.states[im*ny+jm];
let n2 = &self.states[im*ny+j];
let n3 = &self.states[im*ny+jp];
let n4 = &self.states[i*ny+jm];
let n5 = &self.states[i*ny+jp];
let n6 = &self.states[ip*ny+jm];
let n7 = &self.states[ip*ny+j];
let n8 = &self.states[ip*ny+jp];

// see if the cell is alive of dead
let mut k = i;
let mut sk = server_keys[k % server_keys.len()].try_lock();
while let Err(_) = sk {
k += 1;
sk = server_keys[k % server_keys.len()].try_lock();
}
}
is_alive(&sk.unwrap(),
&self.states[i*ny+j],
&vec![n1,n2,n3,n4,n5,n6,n7,n8], zeros)

}).collect();

// update the board
self.states = new_states;
}
}


// config structure
pub struct Config {
pub wait_time_micros: u64,
pub pixel_size: usize,
pub dimensions: (usize, usize),
pub col1: (f32,f32,f32),
pub col2: (f32,f32,f32),
}

impl Config {
pub fn read(fname: &str) -> Result<Config, Box<dyn std::error::Error>> {

let err_message = "Missing argument in the config file";

// load the content
let content = std::fs::read_to_string(fname)?;

// separate the nubers
let mut content = content.split(' ');

// read the content
Ok(Config {
wait_time_micros: content.next().ok_or(err_message)?.parse::<u64>()?,
pixel_size: content.next().ok_or(err_message)?.parse::<usize>()?,
dimensions : (
content.next().ok_or(err_message)?.parse::<usize>()?,
content.next().ok_or(err_message)?.parse::<usize>()?),
col1 : (
content.next().ok_or(err_message)?.parse::<f32>()?,
content.next().ok_or(err_message)?.parse::<f32>()?,
content.next().ok_or(err_message)?.parse::<f32>()?),
col2 : (
content.next().ok_or(err_message)?.parse::<f32>()?,
content.next().ok_or(err_message)?.parse::<f32>()?,
content.next().ok_or(err_message)?.parse::<f32>()?),
})

}
}


/// read a state file, space- and newline-separated
///
/// The file must contain only 0s and 1s and all rows need to have the same length.
pub fn read_csv(fname: &str)
-> Result<((usize, usize), Vec<bool>), Box<dyn std::error::Error>>
{

// load the content
let content = std::fs::read_to_string(fname)?;

// separate in rows
let content = content.split('\n').collect::<Vec<&str>>();
let n_rows = content.len() - 1;

// number of columns
let n_cols = content[0].split(' ').collect::<Vec<&str>>().len() - 1;

// load the data
let mut data = Vec::<bool>::new();
for row in content {
for el in row.split(' ') {
if let Ok(x) = el.parse::<u8>() { data.push(x == 1) };
}
}

Ok(((n_rows, n_cols), data))
}
63 changes: 31 additions & 32 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,45 @@
use homomorphic_game_of_life::*;
use std::sync::Mutex;

fn main() {

// numbers of rows and columns in the board
let (n_rows, n_cols): (usize, usize) = (6,6);


// number of times the server key is duplicated
let n_sk = 16;

// read the dimensions and initial state
let (dimensions, initial_state): ((usize, usize), Vec<bool>)
= read_csv("initial_state.csv").unwrap();

// read the cofig file
let config = Config::read("Config.csv").unwrap();

// set-up the graphical interface
let cb = ggez::ContextBuilder::new("Game of Life", "FM")
.window_setup(conf::WindowSetup::default().title("Conway's Game of Life"))
.window_mode(conf::WindowMode::default().dimensions(
(config.pixel_size*dimensions.1) as f32,
(config.pixel_size*dimensions.0) as f32));
let (ctx, event_loop) = cb.build().unwrap();

// generate the keys
let (client_key, server_key) = gen_keys();

// encrypt false three times
let zeros = (client_key.encrypt(false), client_key.encrypt(false), client_key.encrypt(false));

// initial configuration
let states = vec![true, false, false, false, false, false,
false, true, true, false, false, false,
true, true, false, false, false, false,
false, false, false, false, false, false,
false, false, false, false, false, false,
false, false, false, false, false, false];

// encrypt the initial configuration
let states: Vec<Ciphertext> = states.into_iter().map(|x| client_key.encrypt(x)).collect();
let initial_state: Vec<Ciphertext> = initial_state.into_iter().map(|x| client_key.encrypt(x)).collect();

// build the board
let mut board = Board::new(n_cols, states);

loop {

// show the board
for i in 0..n_rows {
println!("");
for j in 0..n_rows {
if client_key.decrypt(&board.states[i*n_cols+j]) {
print!("█");
} else {
print!("░");
}
}
}
println!("");

// increase the time step
board.evolve(&server_key, &zeros);
let board = Board::new(dimensions.1, initial_state);

// create a vector of server keys for multithreading
let mut server_keys = Vec::<Mutex<ServerKey>>::new();
for _ in 0..n_sk {
server_keys.push(Mutex::new(server_key.clone()));
}

// run the simulation
let state = MainState::new(board, &config, server_keys, zeros, client_key).unwrap();
event::run(ctx, event_loop, state)
}

0 comments on commit 8b11743

Please sign in to comment.