Skip to content

Commit

Permalink
add: example binary impl
Browse files Browse the repository at this point in the history
  • Loading branch information
brech1 committed May 27, 2024
1 parent df4b074 commit 599f66c
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 146 deletions.
224 changes: 80 additions & 144 deletions crates/mpz-circuits-generic/src/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
//!
//! Test module to display an example of a binary circuit representation.
use crate::circuit::{CircuitError, Evaluate, RepresentedValue};
use crate::{
circuit::CircuitError,
model::{Component, Executable, Executor},
};

#[derive(Debug, Copy, Clone, PartialEq)]
/// Binary gate value.
Expand All @@ -13,10 +16,6 @@ pub enum BinaryValue {
One,
}

// Each gate can be performing the same operation multiple times,
// One for each bit of the represented value.
pub type BinaryGateValue = Vec<BinaryValue>;

/// Binary gates.
pub enum BinaryOperation {
/// AND Operation.
Expand All @@ -35,96 +34,6 @@ impl BinaryOperation {
Self::NOT => 1,
}
}

/// Performs the operation on the given inputs.
pub fn evaluate(&self, inputs: &[&BinaryGateValue]) -> Result<BinaryGateValue, CircuitError> {
match self {
Self::AND => Ok(inputs[0]
.iter()
.zip(inputs[1])
.map(|(&a, &b)| {
if a == BinaryValue::One && b == BinaryValue::One {
BinaryValue::One
} else {
BinaryValue::Zero
}
})
.collect()),
Self::NOT => Ok(inputs[0]
.iter()
.map(|&x| {
if x == BinaryValue::Zero {
BinaryValue::One
} else {
BinaryValue::Zero
}
})
.collect()),
Self::XOR => Ok(inputs[0]
.iter()
.zip(inputs[1])
.map(|(&a, &b)| {
if a != b {
BinaryValue::One
} else {
BinaryValue::Zero
}
})
.collect()),
}
}
}

/// Binary circuit representation value.
/// Used as interface for the circuit
#[derive(Debug, PartialEq)]
pub enum BinaryCircuitReprValue {
/// Bool value,
Bool(bool),
/// u8 value.
U8(u8),
}

// Implement the binary circuit represented value.
impl RepresentedValue<BinaryGateValue> for BinaryCircuitReprValue {
fn from_value(value: &BinaryGateValue) -> Result<Self, CircuitError> {
match value.len() {
1 => {
let bit = value[0];
Ok(BinaryCircuitReprValue::Bool(bit == BinaryValue::One))
}
8 => {
let byte = value.iter().fold(0, |acc, &bit| {
(acc << 1) | (if bit == BinaryValue::One { 1 } else { 0 })
});
Ok(BinaryCircuitReprValue::U8(byte as u8))
}
_ => Err(CircuitError::ConversionError),
}
}

fn to_value(&self) -> Result<BinaryGateValue, CircuitError> {
match *self {
BinaryCircuitReprValue::Bool(b) => Ok(vec![if b {
BinaryValue::One
} else {
BinaryValue::Zero
}]),
BinaryCircuitReprValue::U8(byte) => {
let bits = (0..8)
.rev()
.map(|i| {
if byte & (1 << i) != 0 {
BinaryValue::One
} else {
BinaryValue::Zero
}
})
.collect();
Ok(bits)
}
}
}
}

/// Binary gate.
Expand All @@ -137,81 +46,108 @@ pub struct BinaryGate {
op: BinaryOperation,
}

impl Evaluate<BinaryGateValue> for BinaryGate {
fn evaluate(&self, feeds: &mut Vec<Option<BinaryGateValue>>) -> Result<(), CircuitError> {
let input_values = self
.inputs
.iter()
.map(|&idx| {
feeds
.get(idx)
.and_then(|v| v.as_ref())
.ok_or(CircuitError::MissingNodeValue(idx))
})
.collect::<Result<Vec<&BinaryGateValue>, _>>()?;

if input_values.len() != self.op.input_count() {
return Err(CircuitError::InvalidGateInputCount(
self.op.input_count(),
input_values.len(),
));
}
impl Component for BinaryGate {
fn get_inputs(&self) -> Vec<usize> {
self.inputs.clone()
}

let result = self.op.evaluate(&input_values)?;
fn get_outputs(&self) -> Vec<usize> {
vec![self.output]
}
}

// Resize the feeds vector if the output index is out of bounds
// This is the only reason that evaluate receives a vec instead of a slice.
if feeds.get_mut(self.output).is_none() {
feeds.resize(self.output + 1, None);
}
impl<T> Executable<T> for BinaryGate {
type Error = CircuitError;
}

if let Some(output) = feeds.get_mut(self.output) {
*output = Some(result);
} else {
return Err(CircuitError::OutputIndexOutOfRange(self.output));
impl Executor<BinaryValue, BinaryGate> for BinaryGate {
/// User defined custom execution.
fn custom_execution(
&self,
executable: &BinaryGate,
memory: &mut [BinaryValue],
) -> Result<(), CircuitError> {
let input_values = executable
.get_inputs()
.iter()
.map(|&idx| memory.get(idx).ok_or(CircuitError::MissingNodeValue(idx)))
.collect::<Result<Vec<&BinaryValue>, _>>()?;

if input_values.len() != executable.op.input_count() {
return Err(CircuitError::InvalidGateInputCount);
}

let result = match executable.op {
BinaryOperation::AND => {
if *input_values[0] == BinaryValue::One && *input_values[1] == BinaryValue::One {
BinaryValue::One
} else {
BinaryValue::Zero
}
}
BinaryOperation::NOT => {
if *input_values[0] == BinaryValue::Zero {
BinaryValue::One
} else {
BinaryValue::Zero
}
}
BinaryOperation::XOR => {
if *input_values[0] != *input_values[1] {
BinaryValue::One
} else {
BinaryValue::Zero
}
}
};

memory[executable.get_outputs()[0]] = result;

Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::circuit::Circuit;
use crate::circuit::{CircuitBuilder, SequentialExecutor};

#[test]
fn test_circuit() {
// Setup circuit
let mut circuit = Circuit::<BinaryCircuitReprValue, BinaryGate, BinaryGateValue>::new();
// Build memory
let mut memory: Vec<BinaryValue> = Vec::with_capacity(3);

// Add inputs to memory
let input_a = BinaryValue::One;
let input_b = BinaryValue::One;

memory.push(input_a);
memory.push(input_b);
memory.push(BinaryValue::Zero); // Output

// Setup circuit builder
let mut circuit_builder = CircuitBuilder::<BinaryGate>::new();

// Add gate
let gate = BinaryGate {
inputs: vec![0, 1],
output: 2,
op: BinaryOperation::AND,
};
circuit.add_gate(gate);

let input_a: u8 = 0b10101010;
let input_b: u8 = 0b00001111;

let repr_input_a = BinaryCircuitReprValue::U8(input_a);
let repr_input_b = BinaryCircuitReprValue::U8(input_b);
circuit_builder.add_gate(gate);

// Add inputs to the circuit
circuit.add_input(repr_input_a);
circuit.add_input(repr_input_b);
// Build circuit
let circuit = circuit_builder.build().unwrap();

// Define output index
circuit.add_output(2);
// Use the sequential executor for circuit
let executor = SequentialExecutor;
executor
.run_executable(memory.as_mut_slice(), circuit)
.unwrap();

// Expected output
let expected_output: u8 = input_a & input_b;
let repr_expected_output = BinaryCircuitReprValue::U8(expected_output);
let expected_output_and = BinaryValue::One;

let output_values = circuit.run().unwrap();
assert_eq!(output_values.len(), 1);
assert_eq!(output_values[0], repr_expected_output);
assert_eq!(memory[2], expected_output_and);
}
}
6 changes: 4 additions & 2 deletions crates/mpz-circuits-generic/src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,20 @@ where
/// Circuit errors.
#[derive(Debug, Error)]
pub enum CircuitError {
#[error("Circuit execution error")]
CircuitExecutionError,
#[error("Cycle detected involving gate {0}")]
CycleDetected(usize),
#[error("Gate execution failed: {0}")]
GateExecutionError(String),
#[error("Generic circuit error: {0}")]
GenericCircuitError(String),
#[error("Invalid gate input count")]
InvalidGateInputCount,
#[error("Missing node value at index {0}")]
MissingNodeValue(usize),
#[error("Output index out of range: {0}")]
OutputIndexOutOfRange(usize),
#[error("Topological sort failed")]
TopologicalSortFailed,
#[error("Circuit execution error")]
CircuitExecutionError,
}
1 change: 1 addition & 0 deletions crates/mpz-circuits-generic/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod binary;
pub mod circuit;
pub mod model;

0 comments on commit 599f66c

Please sign in to comment.