From 06785e6bd6102b33336f460a77113e4839fe13bd Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Mon, 15 Jul 2024 23:17:09 -0700 Subject: [PATCH 01/42] draft MLV --- chorus_lib/src/core.rs | 115 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 5ac5beb..229d406 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -102,6 +102,41 @@ where } } +/// Represents a value located at multiple locations. +pub struct MultiplyLocated +where + L: LocationSet, +{ + value: Option, + phantom: PhantomData, +} + +impl MultiplyLocated +where + L: LocationSet, +{ + /// Constructs a struct located at the current location with value + pub fn local(value: V) -> Self { + MultiplyLocated { + value: Some(value), + phantom: PhantomData, + } + } +} + +impl Superposition for MultiplyLocated +where + L: LocationSet, +{ + /// Constructs a struct located at another location + fn remote() -> Self { + MultiplyLocated { + value: None, + phantom: PhantomData, + } + } +} + // --- HList and Helpers --- /// heterogeneous list @@ -276,6 +311,25 @@ pub trait ChoreoOp { where L1: Member; + /// TODO: documentation + fn multicast( + &self, + sender: L1, + data: Located, + destinations: D, + ) -> MultiplyLocated + where + L1: Member, + D: Subset; + + /// TODO: documentation + fn naked(&self, data: MultiplyLocated) -> V + where + L: Subset; + + /// TODO: documentation + fn unnaked(&self, data: V) -> MultiplyLocated; + /// Calls a choreography. fn call>(&self, choreo: C) -> R where @@ -461,6 +515,47 @@ where } } + fn multicast( + &self, + _sender: L1, + data: Located, + _destinations: D, + ) -> MultiplyLocated { + if L1::name() == T::name() { + for dest in D::to_string_list() { + if T::name() != dest { + self.transport + .send(&T::name(), dest, data.value.as_ref().unwrap()); + } + } + return MultiplyLocated { + value: data.value, + phantom: PhantomData, + }; + } else { + let mut is_receiver = false; + for dest in D::to_string_list() { + if T::name() == dest { + is_receiver = true; + } + } + if is_receiver { + let v = self.transport.receive(L1::name(), T::name()); + return MultiplyLocated::local(v); + } else { + return MultiplyLocated::remote(); + } + } + } + + fn naked(&self, data: MultiplyLocated) -> V { + return data.value.unwrap(); + } + + fn unnaked(&self, data: V) -> MultiplyLocated { + return MultiplyLocated::local(data); + } + fn call>(&self, choreo: C) -> R where M: LocationSet + Subset, @@ -576,6 +671,26 @@ impl Runner { data.value.unwrap() } + fn multicast( + &self, + _sender: L1, + data: Located, + _destinations: D, + ) -> MultiplyLocated { + MultiplyLocated { + value: data.value, + phantom: PhantomData, + } + } + + fn naked(&self, data: MultiplyLocated) -> V { + return data.value.unwrap(); + } + + fn unnaked(&self, data: V) -> MultiplyLocated { + return MultiplyLocated::local(data); + } + fn call>(&self, choreo: C) -> R where M: LocationSet + Subset, From 7d44c7e3ead9aea1782cf5a611229b00c7441585 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Sun, 21 Jul 2024 10:57:31 -0700 Subject: [PATCH 02/42] multicast with MulticastBuilder as an argument --- chorus_lib/examples/multicast.rs | 82 ++++++++++++++++++++++++++++++++ chorus_lib/src/core.rs | 67 +++++++++++++++++++++----- 2 files changed, 136 insertions(+), 13 deletions(-) create mode 100644 chorus_lib/examples/multicast.rs diff --git a/chorus_lib/examples/multicast.rs b/chorus_lib/examples/multicast.rs new file mode 100644 index 0000000..da76335 --- /dev/null +++ b/chorus_lib/examples/multicast.rs @@ -0,0 +1,82 @@ +extern crate chorus_lib; + +use std::thread; + +use chorus_lib::core::{ + ChoreoOp, Choreography, ChoreographyLocation, LocationSet, MulticastBuilder, Projector, +}; +use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; + +// --- Define two locations (Alice and Bob) --- + +#[derive(ChoreographyLocation)] +struct Alice; + +#[derive(ChoreographyLocation)] +struct Bob; + +#[derive(ChoreographyLocation)] +struct Carol; + +// --- Define a choreography --- +struct MulticastChoreography; + +// Implement the `Choreography` trait for `HelloWorldChoreography` +impl Choreography for MulticastChoreography { + // Define the set of locations involved in the choreography. + // In this case, the set consists of `Alice` and `Bob` and + // the choreography can use theses locations. + type L = LocationSet!(Alice, Bob, Carol); + fn run(self, op: &impl ChoreoOp) { + // Create a located value at Alice + let msg_at_alice = op.locally(Alice, |_| { + println!("Hello from Alice!"); + "Hello from Alice!".to_string() + }); + let msg_at_bob_and_carol = + op.multicast(MulticastBuilder::new(Alice, msg_at_alice).to(Bob).to(Carol)); + op.locally(Bob, |un| { + let msg = un.unwrap2(&msg_at_bob_and_carol); + println!("Bob received: {}", msg); + }); + op.locally(Carol, |un| { + let msg = un.unwrap2(&msg_at_bob_and_carol); + println!("Carol received: {}", msg); + }); + } +} + +fn main() { + let mut handles: Vec> = Vec::new(); + // Create a transport channel + let transport_channel = LocalTransportChannelBuilder::new() + .with(Alice) + .with(Bob) + .with(Carol) + .build(); + // Run the choreography in two threads + { + let transport = LocalTransport::new(Alice, transport_channel.clone()); + handles.push(thread::spawn(move || { + let p = Projector::new(Alice, transport); + p.epp_and_run(MulticastChoreography); + })); + } + { + let transport = LocalTransport::new(Bob, transport_channel.clone()); + handles.push(thread::spawn(move || { + let p = Projector::new(Bob, transport); + p.epp_and_run(MulticastChoreography); + })); + } + { + let transport = LocalTransport::new(Carol, transport_channel.clone()); + handles.push(thread::spawn(move || { + let p = Projector::new(Carol, transport); + p.epp_and_run(MulticastChoreography); + })); + } + for h in handles { + h.join().unwrap(); + } +} diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 229d406..e0599b2 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -263,6 +263,50 @@ impl Unwrapper { pub fn unwrap<'a, V>(&self, located: &'a Located) -> &'a V { located.value.as_ref().unwrap() } + /// TODO: documentation + pub fn unwrap2<'a, V, S: LocationSet, Index>(&self, mlv: &'a MultiplyLocated) -> &'a V + where + L1: Member, + { + mlv.value.as_ref().unwrap() + } +} + +/// TODO: documentation +pub struct MulticastBuilder { + data: Located, + phantom: PhantomData<(L, D)>, +} + +impl MulticastBuilder { + /// Constructs a `MulticastBuilder` struct. + /// + /// - `source` is the source location of the multicast. + /// - `data` is the located value to be multicast. + pub fn new(source: S, data: Located) -> MulticastBuilder { + MulticastBuilder { + data, + phantom: PhantomData, + } + } +} + +impl + MulticastBuilder +{ + /// Adds a destination to the multicast. + pub fn to( + self, + _destination: D1, + ) -> MulticastBuilder> + where + D1: Member, + { + MulticastBuilder { + data: self.data, + phantom: PhantomData, + } + } } /// Provides choreographic operations. @@ -314,9 +358,7 @@ pub trait ChoreoOp { /// TODO: documentation fn multicast( &self, - sender: L1, - data: Located, - destinations: D, + builder: MulticastBuilder, ) -> MultiplyLocated where L1: Member, @@ -517,19 +559,20 @@ where fn multicast( &self, - _sender: L1, - data: Located, - _destinations: D, + builder: MulticastBuilder, ) -> MultiplyLocated { if L1::name() == T::name() { for dest in D::to_string_list() { if T::name() != dest { - self.transport - .send(&T::name(), dest, data.value.as_ref().unwrap()); + self.transport.send( + &T::name(), + dest, + builder.data.value.as_ref().unwrap(), + ); } } return MultiplyLocated { - value: data.value, + value: builder.data.value, phantom: PhantomData, }; } else { @@ -673,12 +716,10 @@ impl Runner { fn multicast( &self, - _sender: L1, - data: Located, - _destinations: D, + builder: MulticastBuilder, ) -> MultiplyLocated { MultiplyLocated { - value: data.value, + value: builder.data.value, phantom: PhantomData, } } From 49855c10b693e13aba71b8078cc80781d370f307 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Mon, 22 Jul 2024 11:38:11 -0700 Subject: [PATCH 03/42] add example from he-lambda-small paper --- chorus_lib/examples/enclave-mlv.rs | 137 +++++++++++++++++++++++++++++ chorus_lib/src/core.rs | 6 +- 2 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 chorus_lib/examples/enclave-mlv.rs diff --git a/chorus_lib/examples/enclave-mlv.rs b/chorus_lib/examples/enclave-mlv.rs new file mode 100644 index 0000000..e09078c --- /dev/null +++ b/chorus_lib/examples/enclave-mlv.rs @@ -0,0 +1,137 @@ +use chorus_lib::{ + core::{ + ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, MultiplyLocated, + Projector, Superposition, + }, + transport::local::{LocalTransport, LocalTransportChannelBuilder}, +}; +use rand::Rng; +use serde::{Deserialize, Serialize}; + +type Query = String; + +#[derive(Serialize, Deserialize)] +enum Choice { + Alice, + Bob, +} + +#[derive(ChoreographyLocation)] +struct Alice; + +#[derive(ChoreographyLocation)] +struct Bob; + +#[derive(ChoreographyLocation)] +struct Carol; + +struct MainChoreography; + +impl Choreography for MainChoreography { + type L = LocationSet!(Alice, Bob, Carol); + fn run(self, op: &impl ChoreoOp) { + let choice = op.locally(Alice, |_| { + let mut rng = rand::thread_rng(); + let choice: bool = rng.gen(); + if choice { + Choice::Alice + } else { + Choice::Bob + } + }); + let ChoiceAndQuery(choice, query_at_alice) = op.enclave(ChooseQueryChoreography { + alice_choice: choice, + }); + let query_at_carol = op.comm(Alice, Carol, &query_at_alice); + let response_at_carol = op.locally(Carol, |un| { + let query = un.unwrap(&query_at_carol); + println!("Carol received query: {}", query); + let r = format!("Carol's response to {}", query); + return r; + }); + let response = op.broadcast(Carol, response_at_carol); + op.enclave(TerminalChoreography { choice, response }); + } +} + +#[derive(Superposition)] +struct ChoiceAndQuery( + MultiplyLocated, + Located, +); + +struct ChooseQueryChoreography { + alice_choice: Located, +} + +impl Choreography for ChooseQueryChoreography { + type L = LocationSet!(Alice, Bob); + fn run(self, op: &impl ChoreoOp) -> ChoiceAndQuery { + let choice = op.broadcast(Alice, self.alice_choice); + let query = match choice { + Choice::Alice => op.locally(Alice, |_| "Alice's query".to_string()), + Choice::Bob => { + let bob_query = op.locally(Bob, |_| "Bob's query".to_string()); + op.comm(Bob, Alice, &bob_query) + } + }; + return ChoiceAndQuery(op.unnaked(choice), query); + } +} + +struct TerminalChoreography { + choice: MultiplyLocated, + response: String, +} + +impl Choreography for TerminalChoreography { + type L = LocationSet!(Alice, Bob); + fn run(self, op: &impl ChoreoOp) { + let choice = op.naked(self.choice); + match choice { + Choice::Alice => { + op.locally(Alice, |_| { + println!("Alice received response: {}", self.response); + }); + } + Choice::Bob => { + op.locally(Bob, |_| { + println!("Bob received response: {}", self.response); + }); + } + } + } +} + +fn main() { + let mut handles = Vec::new(); + let transport_channel = LocalTransportChannelBuilder::new() + .with(Alice) + .with(Bob) + .with(Carol) + .build(); + { + let transport = LocalTransport::new(Alice, transport_channel.clone()); + handles.push(std::thread::spawn(move || { + let p = Projector::new(Alice, transport); + p.epp_and_run(MainChoreography); + })); + } + { + let transport = LocalTransport::new(Bob, transport_channel.clone()); + handles.push(std::thread::spawn(move || { + let p = Projector::new(Bob, transport); + p.epp_and_run(MainChoreography); + })); + } + { + let transport = LocalTransport::new(Carol, transport_channel.clone()); + handles.push(std::thread::spawn(move || { + let p = Projector::new(Carol, transport); + p.epp_and_run(MainChoreography); + })); + } + for handle in handles { + handle.join().unwrap(); + } +} diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index e0599b2..e6660f2 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -370,7 +370,7 @@ pub trait ChoreoOp { L: Subset; /// TODO: documentation - fn unnaked(&self, data: V) -> MultiplyLocated; + fn unnaked(&self, data: V) -> MultiplyLocated; /// Calls a choreography. fn call>(&self, choreo: C) -> R @@ -595,7 +595,7 @@ where return data.value.unwrap(); } - fn unnaked(&self, data: V) -> MultiplyLocated { + fn unnaked(&self, data: V) -> MultiplyLocated { return MultiplyLocated::local(data); } @@ -728,7 +728,7 @@ impl Runner { return data.value.unwrap(); } - fn unnaked(&self, data: V) -> MultiplyLocated { + fn unnaked(&self, data: V) -> MultiplyLocated { return MultiplyLocated::local(data); } From 066d130e940be48c7345719565d9fd258a596b3f Mon Sep 17 00:00:00 2001 From: Mako Bates <33739499+ShapeOfMatter@users.noreply.github.com> Date: Tue, 30 Jul 2024 17:24:02 -0400 Subject: [PATCH 04/42] WIP: add tests, doesnt' seem to work... --- chorus_lib/examples/bookseller.rs | 76 +++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 14 deletions(-) diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index 2237be3..e2c89f1 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -3,7 +3,10 @@ extern crate chorus_lib; use std::io; use std::thread; -use chorus_lib::transport::local::LocalTransportChannelBuilder; +use chorus_lib::{ + core::{Located}, + transport::local::{LocalTransportChannelBuilder}, +}; use chrono::NaiveDate; use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, LocationSet, Projector}; @@ -25,17 +28,14 @@ struct Seller; #[derive(ChoreographyLocation)] struct Buyer; -struct BooksellerChoreography; -impl Choreography for BooksellerChoreography { +struct BooksellerChoreography{ + title: Located, + budget: Located, +} +impl Choreography for BooksellerChoreography { type L = LocationSet!(Seller, Buyer); - fn run(self, op: &impl ChoreoOp) { - let title_at_buyer = op.locally(Buyer, |_| { - println!("Enter the title of the book to buy (TAPL or HoTT)"); - let mut title = String::new(); - io::stdin().read_line(&mut title).unwrap(); - title - }); - let title_at_seller = op.comm(Buyer, Seller, &title_at_buyer); + fn run(self, op: &impl ChoreoOp) -> bool { + let title_at_seller = op.comm(Buyer, Seller, &self.title); let price_at_seller = op.locally(Seller, |un| { let title = un.unwrap(&title_at_seller); if let Some((price, _)) = get_book(&title) { @@ -47,7 +47,7 @@ impl Choreography for BooksellerChoreography { let decision_at_buyer = op.locally(Buyer, |un| { if let Some(price) = un.unwrap(&price_at_buyer) { println!("Price is {}", price); - return *price < BUDGET; + return *price < *un.unwrap(&self.budget); } println!("The book does not exist"); return false; @@ -69,10 +69,15 @@ impl Choreography for BooksellerChoreography { println!("The buyer cannot buy the book"); }); } + decision } } fn main() { + println!("Enter the title of the book to buy (TAPL or HoTT)"); + let mut title = String::new(); + io::stdin().read_line(&mut title).unwrap(); + let transport_channel = LocalTransportChannelBuilder::new() .with(Seller) .with(Buyer) @@ -85,12 +90,55 @@ fn main() { let mut handles: Vec> = Vec::new(); handles.push(thread::spawn(move || { - seller_projector.epp_and_run(BooksellerChoreography); + seller_projector.epp_and_run(BooksellerChoreography{ + title: seller_projector.remote(Buyer), + budget: seller_projector.remote(Buyer), + }); })); handles.push(thread::spawn(move || { - buyer_projector.epp_and_run(BooksellerChoreography); + buyer_projector.epp_and_run(BooksellerChoreography{ + title: buyer_projector.local(title), + budget: buyer_projector.local(BUDGET), + }); })); for h in handles { h.join().unwrap(); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn distributed_tapl() { + let title = String::new("TAPL"); + + let transport_channel = LocalTransportChannelBuilder::new() + .with(Seller) + .with(Buyer) + .build(); + let transport_seller = LocalTransport::new(Seller, transport_channel.clone()); + let transport_buyer = LocalTransport::new(Buyer, transport_channel.clone()); + + let seller_projector = Projector::new(Seller, transport_seller); + let buyer_projector = Projector::new(Buyer, transport_buyer); + + let mut handles: Vec> = Vec::new(); + handles.push(thread::spawn(move || { + seller_projector.epp_and_run(BooksellerChoreography{ + title: seller_projector.remote(Buyer), + budget: seller_projector.remote(Buyer), + }); + })); + handles.push(thread::spawn(move || { + buyer_projector.epp_and_run(BooksellerChoreography{ + title: buyer_projector.local(title), + budget: buyer_projector.local(BUDGET), + }); + })); + for h in handles { + assert!(h.join().unwrap()); + } + } +} From 89bd5acaacc3c8b09951f5d90785a12d0f7a3f66 Mon Sep 17 00:00:00 2001 From: Mako Bates <33739499+ShapeOfMatter@users.noreply.github.com> Date: Fri, 2 Aug 2024 14:43:52 -0400 Subject: [PATCH 05/42] integration test WIP --- chorus_lib/tests/booksellers.rs | 229 ++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 chorus_lib/tests/booksellers.rs diff --git a/chorus_lib/tests/booksellers.rs b/chorus_lib/tests/booksellers.rs new file mode 100644 index 0000000..ee495ef --- /dev/null +++ b/chorus_lib/tests/booksellers.rs @@ -0,0 +1,229 @@ +extern crate chorus_lib; + +use std::collections::HashMap; +use std::sync::Arc; +use std::thread; + +use chorus_lib::{ + core::{ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, Projector, Runner}, + transport::local::{LocalTransport, LocalTransportChannelBuilder}, +}; +use chrono::NaiveDate; + +#[derive(ChoreographyLocation)] +struct Buyer1; + +#[derive(ChoreographyLocation)] +struct Buyer2; + +#[derive(ChoreographyLocation)] +struct Seller; + +type Money = i32; +type Title = String; + +type Inventory = HashMap; + +type Decider = fn(Located, Buyer1>) -> Box, L = LocationSet!(Buyer1, Buyer2)>>; + +fn unilateral(budget: Located) -> Decider { + |price| { + struct Choreo{ + price: Located, + budget: Located, + } + impl Choreography> for Choreo { + type L = LocationSet!(Buyer1, Buyer2); + fn run(self, op: &impl ChoreoOp) -> Located { + op.locally(Buyer1, |un| { + un.unwrap(&self.price) <= un.unwrap(&self.budget) + }) + } + } + Box::new(Choreo{price, budget}) + } +} + +//fn collaborative(budget1: Located, budget2: Located) -> Decider { +// |price| { +// impl Choreography> for TwoBuyerDecider { + // type L = LocationSet!(Buyer1, Buyer2); + // fn run(self, op: &impl ChoreoOp) -> Located { + // let remaining = op.locally(Buyer1, |un| { + // const BUYER1_BUDGET: Money = 100; + // return un.unwrap(&self.price) - BUYER1_BUDGET; + // }); + // let remaining = op.comm(Buyer1, Buyer2, &remaining); + //let decision = op.locally(Buyer2, |un| { + // const BUYER2_BUDGET: Money = 200; + // return *un.unwrap(&remaining) < BUYER2_BUDGET; + // }); + // op.comm(Buyer2, Buyer1, &decision) + //} +//} +// } +//} + + +struct BooksellerChoreography{ + decider: Decider, + inventory: Located, + title: Located, +} + +impl Choreography> for BooksellerChoreography +{ + type L = LocationSet!(Buyer1, Buyer2, Seller); + fn run(self, op: &impl ChoreoOp) -> Option { + let title_at_seller = op.comm(Buyer1, Seller, &self.title); + let price_at_seller = op.locally(Seller, |un| { + let inventory = un.unwrap(&self.inventory); + let title = un.unwrap(&title_at_seller); + match inventory.get(title) { + Some((price, _)) => Some(price), + None => None, + } + }); + let price_at_buyer1 = op.comm(Seller, Buyer1, &price_at_seller); + let decision_at_buyer1 = op.enclave(&self.decider(price_at_buyer1)); + + struct GetDeliveryDateChoreography { + inventory: Located, + title_at_seller: Located, + decision_at_buyer1: Located, + } + impl Choreography, Buyer1>> for GetDeliveryDateChoreography { + type L = LocationSet!(Buyer1, Seller); + fn run(self, op: &impl ChoreoOp) -> Located, Buyer1> { + let decision = op.broadcast(Buyer1, self.decision_at_buyer1); + if decision { + let delivery_date_at_seller = op.locally(Seller, |un| { + let title = un.unwrap(&self.title_at_seller); + let inventory = un.unwrap(&self.inventory); + match inventory.get(title) { + Some((_, delivery_date)) => Some(delivery_date), + None => None, + } + }); + let delivery_date_at_buyer1 = op.comm(Seller, Buyer1, &delivery_date_at_seller); + return delivery_date_at_buyer1; + } else { + return op.locally(Buyer1, |_| None); + } + } + } + + return op.enclave(GetDeliveryDateChoreography { + inventory: self.inventory.clone(), + title_at_seller: title_at_seller.clone(), + decision_at_buyer1, + }); + } +} + +/*fn locate(a: A, p: P) -> Located { + struct Dummy { + a: RC, + p: P1 + } + impl Choreography> for Dummy + where P2: ChoreographyLocation { + type L = LocationSet!(P2); + fn run(self, op: &impl ChoreoOp) -> Located { + op.locally(self.p, |_| self.a) + } + } + Runner::new().run(Dummy{a, p}); +}*/ + +fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option, answer: Option) { + let central_runner = Runner::new(); + let decider: Decider = match budget2 { + Some(b2) => panic!(), + None => unilateral(central_runner.local(budget1)), + }; + + let central_result = central_runner.run(BooksellerChoreography{ + decider, + inventory: central_runner.local(inventory), + title: central_runner.local(title), + }); + assert_eq!(central_result, answer); + + let transport_channel = LocalTransportChannelBuilder::new() + .with(Seller).with(Buyer1).with(Buyer2) + .build(); + + let seller_projector = Arc::new(Projector::new( + Seller, + LocalTransport::new(Seller, transport_channel.clone()), + )); + let buyer1_projector = Arc::new(Projector::new( + Buyer1, + LocalTransport::new(Buyer1, transport_channel.clone()), + )); + let buyer2_projector = Arc::new(Projector::new( + Buyer2, + LocalTransport::new(Buyer2, transport_channel.clone()), + )); + let mut handles = Vec::new(); + + if let Some(budget2) = budget2 { + + } else { + { + let seller_projector = seller_projector.clone(); + let inventory = inventory.clone(); + handles.push(thread::spawn(move || { + seller_projector.epp_and_run(BooksellerChoreography { + decider, + inventory: seller_projector.local(inventory), + title: seller_projector.remote(Buyer1), + }); + })); + } + { + let buyer1_projector = buyer1_projector.clone(); + handles.push(thread::spawn(move || { + buyer1_projector.epp_and_run(BooksellerChoreography { + decider, + inventory: buyer1_projector.remote(Seller), + title: buyer1_projector.local("HoTT".to_string()), + }); + })); + } + { + let buyer2_projector = buyer2_projector.clone(); + handles.push(thread::spawn(move || { + buyer2_projector.epp_and_run(BooksellerChoreography { + decider, + inventory: buyer2_projector.remote(Seller), + title: buyer2_projector.remote(Buyer1), + }); + })); + } + } + + for h in handles { + assert_eq!(h.join().unwrap(), answer); + } +} + +#[test] +fn main() { + let inventory = { + let mut i = Inventory::new(); + i.insert( + "TAPL".to_string(), + (50, NaiveDate::from_ymd_opt(2023, 8, 3).unwrap()), + ); + i.insert( + "HoTT".to_string(), + (150, NaiveDate::from_ymd_opt(2023, 9, 18).unwrap()), + ); + i + }; + let title = "TAPL".to_string(); + let budget = 300; + run_test(inventory, title, budget, None, Some(NaiveDate::from_ymd_opt(2023, 8, 3).unwrap())) +} From 5d5d98009dc86bb8350eb4c705d4ef8054fd098b Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Mon, 26 Aug 2024 10:27:41 -0700 Subject: [PATCH 06/42] parallel --- chorus_derive/src/lib.rs | 3 ++ chorus_lib/examples/parallel.rs | 69 ++++++++++++++++++++++++++++ chorus_lib/src/core.rs | 79 ++++++++++++++++++++++++++++++++- 3 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 chorus_lib/examples/parallel.rs diff --git a/chorus_derive/src/lib.rs b/chorus_derive/src/lib.rs index 371427e..53e8e12 100644 --- a/chorus_derive/src/lib.rs +++ b/chorus_derive/src/lib.rs @@ -7,6 +7,9 @@ pub fn derive_choreography_location(input: TokenStream) -> TokenStream { let DeriveInput { ident, .. } = parse_macro_input!(input); let output = quote! { impl ChoreographyLocation for #ident { + fn new() -> Self { + Self + } fn name() -> &'static str { stringify!(#ident) } diff --git a/chorus_lib/examples/parallel.rs b/chorus_lib/examples/parallel.rs new file mode 100644 index 0000000..e452a8f --- /dev/null +++ b/chorus_lib/examples/parallel.rs @@ -0,0 +1,69 @@ +extern crate chorus_lib; + +use std::thread; + +use rand::Rng; + +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, LocationSet, Projector}; +use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; + +#[derive(ChoreographyLocation, Debug)] +struct Alice; + +#[derive(ChoreographyLocation, Debug)] +struct Bob; + +#[derive(ChoreographyLocation, Debug)] +struct Carol; + +struct ParallelChoreography; +impl Choreography for ParallelChoreography { + type L = LocationSet!(Alice, Bob, Carol); + fn run(self, op: &impl ChoreoOp) { + let faceted = op.parallel(::new(), || { + // return a random number between 1 and 10 + rand::thread_rng().gen_range(1..=10) + }); + op.locally(Alice, |un| { + let x = un.unwrap3(&faceted); + println!("Alice picked {}", x); + }); + op.locally(Bob, |un| { + let x = un.unwrap3(&faceted); + println!("Bob picked {}", x); + }); + op.locally(Carol, |un| { + let x = un.unwrap3(&faceted); + println!("Carol picked {}", x); + }); + } +} + +fn main() { + let transport_channel = LocalTransportChannelBuilder::new() + .with(Alice) + .with(Bob) + .with(Carol) + .build(); + let transport_alice = LocalTransport::new(Alice, transport_channel.clone()); + let transport_bob = LocalTransport::new(Bob, transport_channel.clone()); + let transport_carol = LocalTransport::new(Carol, transport_channel.clone()); + + let alice_projector = Projector::new(Alice, transport_alice); + let bob_projector = Projector::new(Bob, transport_bob); + let carol_projector = Projector::new(Carol, transport_carol); + + let mut handles: Vec> = Vec::new(); + handles.push(thread::spawn(move || { + alice_projector.epp_and_run(ParallelChoreography); + })); + handles.push(thread::spawn(move || { + bob_projector.epp_and_run(ParallelChoreography); + })); + handles.push(thread::spawn(move || { + carol_projector.epp_and_run(ParallelChoreography); + })); + for handle in handles { + handle.join().unwrap(); + } +} diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index e6660f2..536ed95 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -2,7 +2,7 @@ //! //! This module provides core choreography constructs, such as `Choreography`, `Located`, and `Projector`. -use std::marker::PhantomData; +use std::{collections::HashMap, marker::PhantomData}; use serde::de::DeserializeOwned; // re-export so that users can use derive macros without importing serde @@ -20,6 +20,8 @@ pub use serde::{Deserialize, Serialize}; /// struct Alice; /// ``` pub trait ChoreographyLocation: Copy { + /// Constructs a location. + fn new() -> Self; /// Returns the name of the location as a string. fn name() -> &'static str; } @@ -137,24 +139,40 @@ where } } +/// Represents possibly different values located at multiple locations +#[derive(Debug)] +pub struct Faceted +where + L: LocationSet, +{ + value: HashMap, + phantom: PhantomData, +} + // --- HList and Helpers --- /// heterogeneous list #[doc(hidden)] pub trait LocationSet { + fn new() -> Self; /// returns fn to_string_list() -> Vec<&'static str>; } /// end of HList #[doc(hidden)] +#[derive(Debug)] pub struct HNil; /// An element of HList #[doc(hidden)] +#[derive(Debug)] pub struct HCons(Head, Tail); impl LocationSet for HNil { + fn new() -> Self { + HNil + } fn to_string_list() -> Vec<&'static str> { Vec::new() } @@ -164,6 +182,9 @@ where Head: ChoreographyLocation, Tail: LocationSet, { + fn new() -> Self { + HCons(Head::new(), Tail::new()) + } fn to_string_list() -> Vec<&'static str> { let mut v = Tail::to_string_list(); v.push(Head::name()); @@ -270,6 +291,13 @@ impl Unwrapper { { mlv.value.as_ref().unwrap() } + /// TODO: documentation + pub fn unwrap3<'a, V, S: LocationSet, Index>(&self, faceted: &'a Faceted) -> &'a V + where + L1: Member, + { + faceted.value.get(L1::name()).unwrap() + } } /// TODO: documentation @@ -384,6 +412,15 @@ pub trait ChoreoOp { ) -> R where S: Subset; + + /// Performs parallel computation. + fn parallel( + &self, + locations: S, + computation: impl Fn() -> V, // TODO: add unwrapper for S + ) -> Faceted + where + S: Subset; } /// Represents a choreography. @@ -633,6 +670,27 @@ where } R::remote() } + + fn parallel( + &self, + locations: S, + computation: impl Fn() -> V, // TODO: add unwrapper for S + ) -> Faceted + where + S: Subset, + { + let mut values = HashMap::new(); + for location in S::to_string_list() { + if location == T::name() { + let v = computation(); + values.insert(String::from(location), v); + } + } + Faceted { + value: values, + phantom: PhantomData, + } + } } let op: EppOp<'a, L, L1, LS, B> = EppOp { target: PhantomData::, @@ -747,6 +805,25 @@ impl Runner { let op = RunOp::(PhantomData); choreo.run(&op) } + + fn parallel( + &self, + locations: S, + computation: impl Fn() -> V, // TODO: add unwrapper for S + ) -> Faceted + where + S: Subset, + { + let mut values = HashMap::new(); + for location in S::to_string_list() { + let v = computation(); + values.insert(location.to_string(), v); + } + Faceted { + value: values, + phantom: PhantomData, + } + } } let op: RunOp = RunOp(PhantomData); choreo.run(&op) From fc29189e6038684a8c21b4a4a8e8d3b99953fa24 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Wed, 28 Aug 2024 10:29:54 -0700 Subject: [PATCH 07/42] can write fanout choreography --- chorus_lib/examples/fanout.rs | 89 +++++++ chorus_lib/examples/locationset-fold.rs | 36 +++ chorus_lib/src/core.rs | 316 +++++++++++++++++++++++- 3 files changed, 440 insertions(+), 1 deletion(-) create mode 100644 chorus_lib/examples/fanout.rs create mode 100644 chorus_lib/examples/locationset-fold.rs diff --git a/chorus_lib/examples/fanout.rs b/chorus_lib/examples/fanout.rs new file mode 100644 index 0000000..ace1628 --- /dev/null +++ b/chorus_lib/examples/fanout.rs @@ -0,0 +1,89 @@ +extern crate chorus_lib; + +use std::marker::PhantomData; +use std::thread; + +use rand::Rng; + +use chorus_lib::core::{ + ChoreoOp, Choreography, ChoreographyLocation, FanOutChoreography, HCons, HNil, Here, Located, + LocationSet, LocationSetFoldable, Member, Projector, There, +}; +use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; + +#[derive(ChoreographyLocation, Debug)] +struct Alice; + +#[derive(ChoreographyLocation, Debug)] +struct Bob; + +#[derive(ChoreographyLocation, Debug)] +struct Carol; + +struct FanOut { + phantom: PhantomData<(L, QS)>, +} + +impl FanOutChoreography for FanOut { + type L = L; + type QS = QS; + fn new() -> Self { + FanOut { + phantom: PhantomData, + } + } + fn run( + self, + op: &impl ChoreoOp, + ) -> Located + where + Self::QS: chorus_lib::core::Subset, + Q: Member, + Q: Member, + { + op.locally(Q::new(), |_| String::from(Q::name())) + } +} + +struct ParallelChoreography; +impl Choreography for ParallelChoreography { + type L = LocationSet!(Alice, Bob, Carol); + fn run(self, op: &impl ChoreoOp) { + type L = LocationSet!(Alice, Bob, Carol); + op.fanout( + ::new(), + FanOut { + phantom: PhantomData, + }, + ); + } +} + +fn main() { + let transport_channel = LocalTransportChannelBuilder::new() + .with(Alice) + .with(Bob) + .with(Carol) + .build(); + let transport_alice = LocalTransport::new(Alice, transport_channel.clone()); + let transport_bob = LocalTransport::new(Bob, transport_channel.clone()); + let transport_carol = LocalTransport::new(Carol, transport_channel.clone()); + + let alice_projector = Projector::new(Alice, transport_alice); + let bob_projector = Projector::new(Bob, transport_bob); + let carol_projector = Projector::new(Carol, transport_carol); + + let mut handles: Vec> = Vec::new(); + handles.push(thread::spawn(move || { + alice_projector.epp_and_run(ParallelChoreography); + })); + handles.push(thread::spawn(move || { + bob_projector.epp_and_run(ParallelChoreography); + })); + handles.push(thread::spawn(move || { + carol_projector.epp_and_run(ParallelChoreography); + })); + for handle in handles { + handle.join().unwrap(); + } +} diff --git a/chorus_lib/examples/locationset-fold.rs b/chorus_lib/examples/locationset-fold.rs new file mode 100644 index 0000000..00c1278 --- /dev/null +++ b/chorus_lib/examples/locationset-fold.rs @@ -0,0 +1,36 @@ +use chorus_lib::core::{ + ChoreographyLocation, LocationSet, LocationSetFoldable, LocationSetFolder, Member, Subset, +}; + +#[derive(ChoreographyLocation)] +struct Alice; +#[derive(ChoreographyLocation)] +struct Bob; +#[derive(ChoreographyLocation)] +struct Carol; + +fn main() { + type L = LocationSet!(Alice, Bob, Carol); + type QS = LocationSet!(Bob, Carol); + struct F; + impl LocationSetFolder for F { + type L = L; + type QS = QS; + fn f( + &self, + acc: String, + curr: Q, + ) -> String + where + Self::QS: Subset, + Q: Member, + Q: Member, + { + let mut x = acc.clone(); + x.push_str(Q::name()); + x + } + } + let x = QS::foldr(F {}, String::new()); + println!("{}", x); +} diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 536ed95..003da25 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -151,9 +151,23 @@ where // --- HList and Helpers --- +/// xx +pub trait LocationSetFolder { + /// x + type L: LocationSet; + /// looping over + type QS: LocationSet; + /// x + fn f(&self, acc: B, curr: Q) -> B + where + Self::QS: Subset, + Q: Member, + Q: Member; +} + /// heterogeneous list #[doc(hidden)] -pub trait LocationSet { +pub trait LocationSet: Sized { fn new() -> Self; /// returns fn to_string_list() -> Vec<&'static str>; @@ -169,6 +183,41 @@ pub struct HNil; #[derive(Debug)] pub struct HCons(Head, Tail); +/// x +pub trait LocationSetFoldable { + /// x + fn foldr>(f: F, acc: B) -> B; +} + +impl LocationSetFoldable for HNil { + fn foldr>(f: F, acc: B) -> B { + acc + } +} + +impl< + L: LocationSet, + QS: LocationSet, + Head: ChoreographyLocation, + Tail, + QSSubsetL, + HeadMemberL, + HeadMemberQS, + ITail, + > LocationSetFoldable>>> + for HCons +where + QS: Subset, + Head: Member, + Head: Member, + Tail: LocationSetFoldable, +{ + fn foldr>(f: F, acc: B) -> B { + let x = f.f(acc, Head::new()); + Tail::foldr(f, x) + } +} + impl LocationSet for HNil { fn new() -> Self { HNil @@ -192,6 +241,13 @@ where } } +#[derive(ChoreographyLocation)] +struct Alice; +#[derive(ChoreographyLocation)] +struct Bob; +#[derive(ChoreographyLocation)] +struct Carol; + // To export `LocationSet` under the `core` module, we define an internal macro and export it. // This is because Rust does not allow us to export a macro from a module without re-exporting it. // `__ChoRus_Internal_LocationSet` is the internal macro and it is configured not to be visible in the documentation. @@ -421,6 +477,68 @@ pub trait ChoreoOp { ) -> Faceted where S: Subset; + + /// Performs fanout computation. + fn fanout< + // return value type + V, + // locations looping over + QS: LocationSet, + // FanOut Choreography over L iterating over QS returning V + FOC: FanOutChoreography, + // Proof that QS is a subset of L + QSSubsetL, + QSFoldable, + F: LocationSetFoldable, + >( + &self, + locations: QS, + c: FOC, + ) -> Faceted + where + QS: Subset; + // fn fanout< + // V, + // S: LocationSet, + // Index, + // SF: LocationSetFoldable, + // B: FanOutChoreography, + // >( + // &self, + // locations: S, + // c: B, + // ) -> Faceted; +} + +struct X { + phantom: PhantomData<(L,)>, +} + +/// TODO: documentation +pub trait FanOutChoreography { + /// All locations + type L: LocationSet; + /// Locations looping over + type QS: LocationSet; + /// TODO: documentation + fn new() -> Self; + /// TODO: documentation + fn run( + self, + op: &impl ChoreoOp, + ) -> Located + where + Self::QS: Subset, + Q: Member, + Q: Member; +} + +impl Choreography> for X { + type L = HCons; + fn run(self, op: &impl ChoreoOp) -> Located { + let x = op.locally(L1::new(), |_| 1); + x + } } /// Represents a choreography. @@ -691,6 +809,167 @@ where phantom: PhantomData, } } + + fn fanout< + // return value type + V, + // locations looping over + QS: LocationSet, + // FanOut Choreography over L iterating over QS returning V + FOC: FanOutChoreography, + // Proof that QS is a subset of L + QSSubsetL, + QSFoldable, + F: LocationSetFoldable, + >( + &self, + locations: QS, + c: FOC, + ) -> Faceted + where + QS: Subset, + { + let op: EppOp = EppOp { + target: PhantomData::, + transport: self.transport, + locations: self.transport.locations(), + marker: PhantomData::, + projector_location_set: PhantomData::, + }; + let values = HashMap::new(); + + struct Loop< + 'a, + L: LocationSet, + Target: ChoreographyLocation, + LS: LocationSet, + B: Transport, + V, + QSSubsetL, + QS: LocationSet + Subset, + FOC: FanOutChoreography, + > { + phantom: PhantomData<(V, QS, QSSubsetL, FOC)>, + op: EppOp<'a, L, Target, LS, B>, + } + + impl< + 'a, + L: LocationSet, + Target: ChoreographyLocation, + LS: LocationSet, + B: Transport, + V, + QSSubsetL, + QS: LocationSet + Subset, + FOC: FanOutChoreography, + > LocationSetFolder> + for Loop<'a, L, Target, LS, B, V, QSSubsetL, QS, FOC> + { + type L = L; + type QS = QS; + fn f( + &self, + mut acc: HashMap, + curr: Q, + ) -> HashMap + where + Self::QS: Subset, + Q: Member, + Q: Member, + { + let c = FOC::new(); + let v = c.run::(&self.op); + acc.insert(String::from(Q::name()), v.value.unwrap()); + acc + } + } + let values = F::foldr( + Loop:: { + phantom: PhantomData, + op: op, + }, + values, + ); + Faceted { + value: values, + phantom: PhantomData, + } + } + + // fn fanout< + // V, + // S: LocationSet, + // Index, + // SF: LocationSetFoldable, + // F: FanOutChoreography, + // >( + // &self, + // locations: S, + // c: F, + // ) -> Faceted { + // let op: EppOp = EppOp { + // target: PhantomData::, + // transport: self.transport, + // locations: self.transport.locations(), + // marker: PhantomData::, + // projector_location_set: PhantomData::, + // }; + // let values = HashMap::new(); + // // struct Loop< + // // // parameters required for EppOp + // // 'a, + // // L: LocationSet, // all locations + // // Target: ChoreographyLocation, // target location + // // LS: LocationSet, + // // B: Transport, // transport + // // // Required for FanOutChoreography + // // V, + // // QS: LocationSet, + // // F: FanOutChoreography, + // // > { + // // phantom: PhantomData<(V, QS, F)>, + // // op: EppOp<'a, L, Target, LS, B>, + // // } + // // impl< + // // 'a, + // // L: LocationSet, + // // Target: ChoreographyLocation, + // // LS: LocationSet, + // // B: Transport, + // // V, + // // QS: LocationSet, + // // F: FanOutChoreography, + // // > LocationSetFolder> + // // for Loop<'a, L, Target, LS, B, V, QS, F> + // // { + // // type L = L; + // // fn f( + // // &self, + // // mut acc: HashMap, + // // _curr: I, + // // ) -> HashMap + // // where + // // I: Member, + // // { + // // let c = F::new(); + // // let v = c.run::(&self.op); + // // acc.insert(String::from(I::name()), v.value.unwrap()); + // // acc + // // } + // // } + // // let values = SF::foldr( + // // Loop:: { + // // phantom: PhantomData::<(V, S, F)>, + // // op, + // // }, + // // values, + // // ); + // Faceted { + // value: values, + // phantom: PhantomData, + // } + // } } let op: EppOp<'a, L, L1, LS, B> = EppOp { target: PhantomData::, @@ -824,6 +1103,41 @@ impl Runner { phantom: PhantomData, } } + fn fanout< + // return value type + V, + // locations looping over + QS: LocationSet, + // FanOut Choreography over L iterating over QS returning V + FOC: FanOutChoreography, + // Proof that QS is a subset of L + QSSubsetL, + QSFoldable, + F: LocationSetFoldable, + >( + &self, + locations: QS, + c: FOC, + ) -> Faceted + where + QS: Subset, + { + unimplemented!() + } + + // fn fanout< + // V, + // S: LocationSet, + // Index, + // SF: LocationSetFoldable, + // B: FanOutChoreography, + // >( + // &self, + // locations: S, + // c: B, + // ) -> Faceted { + // unimplemented!() + // } } let op: RunOp = RunOp(PhantomData); choreo.run(&op) From 039096b2733baade20dabb02586224b0ee9061da Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Wed, 28 Aug 2024 10:47:51 -0700 Subject: [PATCH 08/42] finish fanout --- chorus_lib/examples/fanout.rs | 52 +++++++++++++++----- chorus_lib/src/core.rs | 90 ++++------------------------------- 2 files changed, 48 insertions(+), 94 deletions(-) diff --git a/chorus_lib/examples/fanout.rs b/chorus_lib/examples/fanout.rs index ace1628..d7a64e2 100644 --- a/chorus_lib/examples/fanout.rs +++ b/chorus_lib/examples/fanout.rs @@ -7,7 +7,7 @@ use rand::Rng; use chorus_lib::core::{ ChoreoOp, Choreography, ChoreographyLocation, FanOutChoreography, HCons, HNil, Here, Located, - LocationSet, LocationSetFoldable, Member, Projector, There, + LocationSet, LocationSetFoldable, Member, Projector, Subset, There, }; use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; @@ -20,11 +20,33 @@ struct Bob; #[derive(ChoreographyLocation, Debug)] struct Carol; -struct FanOut { - phantom: PhantomData<(L, QS)>, +struct FanOut +where + Alice: Member, +{ + phantom: PhantomData<(L, QS, Alice, AliceMemberL)>, } -impl FanOutChoreography for FanOut { +impl + FanOut +where + Alice: Member, +{ + fn new(x: Alice) -> Self + where + Alice: Member, + { + FanOut { + phantom: PhantomData, + } + } +} + +impl + FanOutChoreography for FanOut +where + Alice: Member, +{ type L = L; type QS = QS; fn new() -> Self { @@ -37,11 +59,15 @@ impl FanOutChoreography for FanOut, ) -> Located where - Self::QS: chorus_lib::core::Subset, + Self::QS: Subset, Q: Member, Q: Member, { - op.locally(Q::new(), |_| String::from(Q::name())) + let msg_at_alice = op.locally(Alice::new(), |_| { + format!("{} says hi to {}", Alice::name(), Q::name()) + }); + let msg_at_q = op.comm(Alice::new(), Q::new(), &msg_at_alice); + msg_at_q } } @@ -49,13 +75,13 @@ struct ParallelChoreography; impl Choreography for ParallelChoreography { type L = LocationSet!(Alice, Bob, Carol); fn run(self, op: &impl ChoreoOp) { - type L = LocationSet!(Alice, Bob, Carol); - op.fanout( - ::new(), - FanOut { - phantom: PhantomData, - }, - ); + let v = op.fanout(::new(), FanOut::new(Alice)); + op.locally(Bob, |un| { + println!("{}", un.unwrap3(&v)); + }); + op.locally(Carol, |un| { + println!("{}", un.unwrap3(&v)); + }); } } diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 003da25..65d3663 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -489,14 +489,14 @@ pub trait ChoreoOp { // Proof that QS is a subset of L QSSubsetL, QSFoldable, - F: LocationSetFoldable, >( &self, locations: QS, c: FOC, ) -> Faceted where - QS: Subset; + QS: Subset, + QS: LocationSetFoldable; // fn fanout< // V, // S: LocationSet, @@ -820,7 +820,6 @@ where // Proof that QS is a subset of L QSSubsetL, QSFoldable, - F: LocationSetFoldable, >( &self, locations: QS, @@ -828,6 +827,7 @@ where ) -> Faceted where QS: Subset, + QS: LocationSetFoldable, { let op: EppOp = EppOp { target: PhantomData::, @@ -880,14 +880,16 @@ where { let c = FOC::new(); let v = c.run::(&self.op); - acc.insert(String::from(Q::name()), v.value.unwrap()); + if Target::name() == Q::name() { + acc.insert(String::from(Q::name()), v.value.unwrap()); + } acc } } - let values = F::foldr( + let values = QS::foldr( Loop:: { phantom: PhantomData, - op: op, + op, }, values, ); @@ -896,80 +898,6 @@ where phantom: PhantomData, } } - - // fn fanout< - // V, - // S: LocationSet, - // Index, - // SF: LocationSetFoldable, - // F: FanOutChoreography, - // >( - // &self, - // locations: S, - // c: F, - // ) -> Faceted { - // let op: EppOp = EppOp { - // target: PhantomData::, - // transport: self.transport, - // locations: self.transport.locations(), - // marker: PhantomData::, - // projector_location_set: PhantomData::, - // }; - // let values = HashMap::new(); - // // struct Loop< - // // // parameters required for EppOp - // // 'a, - // // L: LocationSet, // all locations - // // Target: ChoreographyLocation, // target location - // // LS: LocationSet, - // // B: Transport, // transport - // // // Required for FanOutChoreography - // // V, - // // QS: LocationSet, - // // F: FanOutChoreography, - // // > { - // // phantom: PhantomData<(V, QS, F)>, - // // op: EppOp<'a, L, Target, LS, B>, - // // } - // // impl< - // // 'a, - // // L: LocationSet, - // // Target: ChoreographyLocation, - // // LS: LocationSet, - // // B: Transport, - // // V, - // // QS: LocationSet, - // // F: FanOutChoreography, - // // > LocationSetFolder> - // // for Loop<'a, L, Target, LS, B, V, QS, F> - // // { - // // type L = L; - // // fn f( - // // &self, - // // mut acc: HashMap, - // // _curr: I, - // // ) -> HashMap - // // where - // // I: Member, - // // { - // // let c = F::new(); - // // let v = c.run::(&self.op); - // // acc.insert(String::from(I::name()), v.value.unwrap()); - // // acc - // // } - // // } - // // let values = SF::foldr( - // // Loop:: { - // // phantom: PhantomData::<(V, S, F)>, - // // op, - // // }, - // // values, - // // ); - // Faceted { - // value: values, - // phantom: PhantomData, - // } - // } } let op: EppOp<'a, L, L1, LS, B> = EppOp { target: PhantomData::, @@ -1113,7 +1041,6 @@ impl Runner { // Proof that QS is a subset of L QSSubsetL, QSFoldable, - F: LocationSetFoldable, >( &self, locations: QS, @@ -1121,6 +1048,7 @@ impl Runner { ) -> Faceted where QS: Subset, + QS: LocationSetFoldable, { unimplemented!() } From e94964764b0d9ec8e80d679c38b371f7d12705c2 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Wed, 28 Aug 2024 12:06:07 -0700 Subject: [PATCH 09/42] fix self communication bug --- chorus_lib/examples/fanout.rs | 46 +++++++++++++++++++++++------------ chorus_lib/src/core.rs | 15 +++++++++--- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/chorus_lib/examples/fanout.rs b/chorus_lib/examples/fanout.rs index d7a64e2..4a34753 100644 --- a/chorus_lib/examples/fanout.rs +++ b/chorus_lib/examples/fanout.rs @@ -3,11 +3,9 @@ extern crate chorus_lib; use std::marker::PhantomData; use std::thread; -use rand::Rng; - use chorus_lib::core::{ - ChoreoOp, Choreography, ChoreographyLocation, FanOutChoreography, HCons, HNil, Here, Located, - LocationSet, LocationSetFoldable, Member, Projector, Subset, There, + ChoreoOp, Choreography, ChoreographyLocation, FanOutChoreography, Located, LocationSet, Member, + Projector, Subset, }; use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; @@ -32,7 +30,7 @@ impl where Alice: Member, { - fn new(x: Alice) -> Self + fn new(_: Alice) -> Self where Alice: Member, { @@ -67,6 +65,9 @@ where format!("{} says hi to {}", Alice::name(), Q::name()) }); let msg_at_q = op.comm(Alice::new(), Q::new(), &msg_at_alice); + op.locally(Q::new(), |un| { + println!("{} received: \"{}\"", Q::name(), un.unwrap(&msg_at_q)) + }); msg_at_q } } @@ -75,7 +76,7 @@ struct ParallelChoreography; impl Choreography for ParallelChoreography { type L = LocationSet!(Alice, Bob, Carol); fn run(self, op: &impl ChoreoOp) { - let v = op.fanout(::new(), FanOut::new(Alice)); + let v = op.fanout(::new(), FanOut::new(Bob)); op.locally(Bob, |un| { println!("{}", un.unwrap3(&v)); }); @@ -100,15 +101,30 @@ fn main() { let carol_projector = Projector::new(Carol, transport_carol); let mut handles: Vec> = Vec::new(); - handles.push(thread::spawn(move || { - alice_projector.epp_and_run(ParallelChoreography); - })); - handles.push(thread::spawn(move || { - bob_projector.epp_and_run(ParallelChoreography); - })); - handles.push(thread::spawn(move || { - carol_projector.epp_and_run(ParallelChoreography); - })); + handles.push( + thread::Builder::new() + .name("Alice".to_string()) + .spawn(move || { + alice_projector.epp_and_run(ParallelChoreography); + }) + .unwrap(), + ); + handles.push( + thread::Builder::new() + .name("Bob".to_string()) + .spawn(move || { + bob_projector.epp_and_run(ParallelChoreography); + }) + .unwrap(), + ); + handles.push( + thread::Builder::new() + .name("Carol".to_string()) + .spawn(move || { + carol_projector.epp_and_run(ParallelChoreography); + }) + .unwrap(), + ); for handle in handles { handle.join().unwrap(); } diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 65d3663..4d11514 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -682,6 +682,10 @@ where _receiver: L2, data: &Located, ) -> Located { + if L1::name() == T::name() && L1::name() == L2::name() { + let s = serde_json::to_string(data.value.as_ref().unwrap()).unwrap(); + return Located::local(serde_json::from_str(s.as_str()).unwrap()); + } if L1::name() == T::name() { self.transport .send(L1::name(), L2::name(), data.value.as_ref().unwrap()); @@ -879,10 +883,13 @@ where Q: Member, { let c = FOC::new(); - let v = c.run::(&self.op); - if Target::name() == Q::name() { - acc.insert(String::from(Q::name()), v.value.unwrap()); - } + let v = c.run::(&self.op); + match v.value { + Some(value) => { + acc.insert(String::from(Q::name()), value); + } + None => {} + }; acc } } From 7fecb018a94aade09395f2827eaaf248e7d8fab6 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Sat, 7 Sep 2024 16:17:05 -0700 Subject: [PATCH 10/42] :recycle: --- chorus_lib/src/core.rs | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 4d11514..36f9ea1 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -497,17 +497,6 @@ pub trait ChoreoOp { where QS: Subset, QS: LocationSetFoldable; - // fn fanout< - // V, - // S: LocationSet, - // Index, - // SF: LocationSetFoldable, - // B: FanOutChoreography, - // >( - // &self, - // locations: S, - // c: B, - // ) -> Faceted; } struct X { @@ -1059,20 +1048,6 @@ impl Runner { { unimplemented!() } - - // fn fanout< - // V, - // S: LocationSet, - // Index, - // SF: LocationSetFoldable, - // B: FanOutChoreography, - // >( - // &self, - // locations: S, - // c: B, - // ) -> Faceted { - // unimplemented!() - // } } let op: RunOp = RunOp(PhantomData); choreo.run(&op) From b69cbf660b2a8baf15866daad96c618db63d63f0 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Sat, 7 Sep 2024 16:18:14 -0700 Subject: [PATCH 11/42] cleanup --- chorus_lib/src/core.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 36f9ea1..f68ad52 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -499,10 +499,6 @@ pub trait ChoreoOp { QS: LocationSetFoldable; } -struct X { - phantom: PhantomData<(L,)>, -} - /// TODO: documentation pub trait FanOutChoreography { /// All locations @@ -522,14 +518,6 @@ pub trait FanOutChoreography { Q: Member; } -impl Choreography> for X { - type L = HCons; - fn run(self, op: &impl ChoreoOp) -> Located { - let x = op.locally(L1::new(), |_| 1); - x - } -} - /// Represents a choreography. /// /// The trait for defining a choreography. It should be implemented for a struct that represents a choreography. From 33a8e985a1bba943df0df4e9484cb305fb519321 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Mon, 9 Sep 2024 17:25:01 -0700 Subject: [PATCH 12/42] give type parameters meaningful names --- chorus_lib/src/core.rs | 248 +++++++++++++++++++++++------------------ 1 file changed, 142 insertions(+), 106 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index f68ad52..e7aef3e 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -397,7 +397,7 @@ impl /// /// The trait provides methods to work with located values. An implementation of the trait is "injected" into /// a choreography at runtime and provides the actual implementation of the operators. -pub trait ChoreoOp { +pub trait ChoreoOp { /// Performs a computation at the specified location. /// /// `locally` performs a computation at a location, which are specified by `location` and `computation`, respectively. @@ -412,54 +412,60 @@ pub trait ChoreoOp { computation: impl Fn(Unwrapper) -> V, ) -> Located where - L1: Member; + L1: Member; /// Performs a communication between two locations. /// /// `comm` sends `data` from `sender` to `receiver`. The `data` must be a `Located` struct at the `sender` location /// and the value type must implement `Portable`. - fn comm( + fn comm< + Sender: ChoreographyLocation, + Receiver: ChoreographyLocation, + V: Portable, + Index1, + Index2, + >( &self, - sender: L1, - receiver: L2, - data: &Located, - ) -> Located + sender: Sender, + receiver: Receiver, + data: &Located, + ) -> Located where - L1: Member, - L2: Member; + Sender: Member, + Receiver: Member; /// Performs a broadcast from a location to all other locations. /// /// `broadcast` broadcasts `data` from `sender` to all other locations. The `data` must be a `Located` struct at the `sender` location. /// The method returns the non-located value. - fn broadcast( + fn broadcast( &self, - sender: L1, - data: Located, + sender: Sender, + data: Located, ) -> V where - L1: Member; + Sender: Member; /// TODO: documentation - fn multicast( + fn multicast( &self, - builder: MulticastBuilder, + builder: MulticastBuilder, ) -> MultiplyLocated where - L1: Member, - D: Subset; + Sender: Member, + D: Subset; /// TODO: documentation fn naked(&self, data: MultiplyLocated) -> V where - L: Subset; + ChoreoLS: Subset; /// TODO: documentation - fn unnaked(&self, data: V) -> MultiplyLocated; + fn unnaked(&self, data: V) -> MultiplyLocated; /// Calls a choreography. fn call>(&self, choreo: C) -> R where - M: LocationSet + Subset; + M: LocationSet + Subset; /// Calls a choreography on a subset of locations. fn enclave, Index>( @@ -467,7 +473,7 @@ pub trait ChoreoOp { choreo: C, ) -> R where - S: Subset; + S: Subset; /// Performs parallel computation. fn parallel( @@ -476,7 +482,7 @@ pub trait ChoreoOp { computation: impl Fn() -> V, // TODO: add unwrapper for S ) -> Faceted where - S: Subset; + S: Subset; /// Performs fanout computation. fn fanout< @@ -485,7 +491,7 @@ pub trait ChoreoOp { // locations looping over QS: LocationSet, // FanOut Choreography over L iterating over QS returning V - FOC: FanOutChoreography, + FOC: FanOutChoreography, // Proof that QS is a subset of L QSSubsetL, QSFoldable, @@ -495,13 +501,13 @@ pub trait ChoreoOp { c: FOC, ) -> Faceted where - QS: Subset, - QS: LocationSetFoldable; + QS: Subset, + QS: LocationSetFoldable; } /// TODO: documentation pub trait FanOutChoreography { - /// All locations + /// All locations involved in the choreography type L: LocationSet; /// Locations looping over type QS: LocationSet; @@ -553,26 +559,38 @@ pub trait Transport { } /// Provides a method to perform end-point projection. -pub struct Projector, Index> -where - L1: Member, +pub struct Projector< + // `LS` is a location set supported by the transport + // Projector is capable of projecting any choreographies whose location set is a subset of `LS` + TransportLS: LocationSet, + // `L1` is the projection target + Target: ChoreographyLocation, + // `T` is the transport that supports locations `LS` and for target `L1` + T: Transport, + Index, +> where + Target: Member, { - target: PhantomData, + target: PhantomData, transport: T, - location_set: PhantomData, + location_set: PhantomData, index: PhantomData, } -impl, Index> - Projector +impl< + TransportLS: LocationSet, + Target: ChoreographyLocation, + B: Transport, + Index, + > Projector where - L1: Member, + Target: Member, { /// Constructs a `Projector` struct. /// /// - `target` is the projection target of the choreography. /// - `transport` is an implementation of `Transport`. - pub fn new(target: L1, transport: B) -> Self { + pub fn new(target: Target, transport: B) -> Self { _ = target; Projector { target: PhantomData, @@ -585,16 +603,16 @@ where /// Constructs a `Located` struct located at the projection target using the actual value. /// /// Use this method to run a choreography that takes a located value as an input. - pub fn local(&self, value: V) -> Located { + pub fn local(&self, value: V) -> Located { Located::local(value) } /// Constructs a `Located` struct *NOT* located at the projection target. /// /// Use this method to run a choreography that takes a located value as an input. - pub fn remote(&self, at: L2) -> Located + pub fn remote(&self, at: L) -> Located where - L2: Member<>::Remainder, Index2>, + L: Member<>::Remainder, Index2>, { _ = at; Located::remote() @@ -603,40 +621,52 @@ where /// Unwraps a located value at the projection target. /// /// Use this method to access the located value returned by a choreography. - pub fn unwrap(&self, located: Located) -> V { + pub fn unwrap(&self, located: Located) -> V { located.value.unwrap() } /// Performs end-point projection and runs a choreography. - pub fn epp_and_run<'a, V, L: LocationSet, C: Choreography, IndexSet>( + pub fn epp_and_run< + 'a, + V, + // location set of the choreography to EPP + ChoreoLS: LocationSet, + C: Choreography, + IndexSet, + >( &'a self, choreo: C, ) -> V where - L: Subset, + ChoreoLS: Subset, { struct EppOp< 'a, - L: LocationSet, - L1: ChoreographyLocation, - LS: LocationSet, - B: Transport, + ChoreoLS: LocationSet, // L is a location set associated with the choreography + Target: ChoreographyLocation, + TransportLS: LocationSet, + B: Transport, > { - target: PhantomData, + target: PhantomData, transport: &'a B, locations: Vec<&'static str>, - marker: PhantomData, - projector_location_set: PhantomData, + marker: PhantomData, + projector_location_set: PhantomData, } - impl<'a, L: LocationSet, T: ChoreographyLocation, LS: LocationSet, B: Transport> - ChoreoOp for EppOp<'a, L, T, LS, B> + impl< + 'a, + ChoreoLS: LocationSet, + Target: ChoreographyLocation, + TransportLS: LocationSet, + B: Transport, + > ChoreoOp for EppOp<'a, ChoreoLS, Target, TransportLS, B> { fn locally( &self, _location: L1, computation: impl Fn(Unwrapper) -> V, ) -> Located { - if L1::name() == T::name() { + if L1::name() == Target::name() { let unwrapper = Unwrapper { phantom: PhantomData, }; @@ -659,15 +689,15 @@ where _receiver: L2, data: &Located, ) -> Located { - if L1::name() == T::name() && L1::name() == L2::name() { + if L1::name() == Target::name() && L1::name() == L2::name() { let s = serde_json::to_string(data.value.as_ref().unwrap()).unwrap(); return Located::local(serde_json::from_str(s.as_str()).unwrap()); } - if L1::name() == T::name() { + if L1::name() == Target::name() { self.transport .send(L1::name(), L2::name(), data.value.as_ref().unwrap()); Located::remote() - } else if L2::name() == T::name() { + } else if L2::name() == Target::name() { let value = self.transport.receive(L1::name(), L2::name()); Located::local(value) } else { @@ -680,28 +710,31 @@ where _sender: L1, data: Located, ) -> V { - if L1::name() == T::name() { + if L1::name() == Target::name() { for dest in &self.locations { - if T::name() != *dest { - self.transport - .send(&T::name(), &dest, data.value.as_ref().unwrap()); + if Target::name() != *dest { + self.transport.send( + &Target::name(), + &dest, + data.value.as_ref().unwrap(), + ); } } return data.value.unwrap(); } else { - self.transport.receive(L1::name(), &T::name()) + self.transport.receive(L1::name(), &Target::name()) } } fn multicast( &self, - builder: MulticastBuilder, + builder: MulticastBuilder, ) -> MultiplyLocated { - if L1::name() == T::name() { + if L1::name() == Target::name() { for dest in D::to_string_list() { - if T::name() != dest { + if Target::name() != dest { self.transport.send( - &T::name(), + &Target::name(), dest, builder.data.value.as_ref().unwrap(), ); @@ -714,12 +747,12 @@ where } else { let mut is_receiver = false; for dest in D::to_string_list() { - if T::name() == dest { + if Target::name() == dest { is_receiver = true; } } if is_receiver { - let v = self.transport.receive(L1::name(), T::name()); + let v = self.transport.receive(L1::name(), Target::name()); return MultiplyLocated::local(v); } else { return MultiplyLocated::remote(); @@ -731,20 +764,20 @@ where return data.value.unwrap(); } - fn unnaked(&self, data: V) -> MultiplyLocated { + fn unnaked(&self, data: V) -> MultiplyLocated { return MultiplyLocated::local(data); } fn call>(&self, choreo: C) -> R where - M: LocationSet + Subset, + M: LocationSet + Subset, { - let op: EppOp<'a, M, T, LS, B> = EppOp { - target: PhantomData::, + let op: EppOp<'a, M, Target, TransportLS, B> = EppOp { + target: PhantomData::, transport: &self.transport, locations: self.transport.locations(), marker: PhantomData::, - projector_location_set: PhantomData::, + projector_location_set: PhantomData::, }; choreo.run(&op) } @@ -756,13 +789,13 @@ where let locs_vec = S::to_string_list(); for location in &locs_vec { - if *location == T::name().to_string() { + if *location == Target::name().to_string() { let op = EppOp { - target: PhantomData::, + target: PhantomData::, transport: self.transport, locations: locs_vec, marker: PhantomData::, - projector_location_set: PhantomData::, + projector_location_set: PhantomData::, }; return choreo.run(&op); } @@ -776,11 +809,11 @@ where computation: impl Fn() -> V, // TODO: add unwrapper for S ) -> Faceted where - S: Subset, + S: Subset, { let mut values = HashMap::new(); for location in S::to_string_list() { - if location == T::name() { + if location == Target::name() { let v = computation(); values.insert(String::from(location), v); } @@ -797,7 +830,7 @@ where // locations looping over QS: LocationSet, // FanOut Choreography over L iterating over QS returning V - FOC: FanOutChoreography, + FOC: FanOutChoreography, // Proof that QS is a subset of L QSSubsetL, QSFoldable, @@ -807,47 +840,47 @@ where c: FOC, ) -> Faceted where - QS: Subset, - QS: LocationSetFoldable, + QS: Subset, + QS: LocationSetFoldable, { - let op: EppOp = EppOp { - target: PhantomData::, + let op: EppOp = EppOp { + target: PhantomData::, transport: self.transport, locations: self.transport.locations(), - marker: PhantomData::, - projector_location_set: PhantomData::, + marker: PhantomData::, + projector_location_set: PhantomData::, }; let values = HashMap::new(); struct Loop< 'a, - L: LocationSet, + ChoreoLS: LocationSet, Target: ChoreographyLocation, - LS: LocationSet, - B: Transport, + TransportLS: LocationSet, + B: Transport, V, QSSubsetL, - QS: LocationSet + Subset, - FOC: FanOutChoreography, + QS: LocationSet + Subset, + FOC: FanOutChoreography, > { phantom: PhantomData<(V, QS, QSSubsetL, FOC)>, - op: EppOp<'a, L, Target, LS, B>, + op: EppOp<'a, ChoreoLS, Target, TransportLS, B>, } impl< 'a, - L: LocationSet, + ChoreoLS: LocationSet, Target: ChoreographyLocation, - LS: LocationSet, - B: Transport, + TransportLS: LocationSet, + B: Transport, V, QSSubsetL, - QS: LocationSet + Subset, - FOC: FanOutChoreography, + QS: LocationSet + Subset, + FOC: FanOutChoreography, > LocationSetFolder> - for Loop<'a, L, Target, LS, B, V, QSSubsetL, QS, FOC> + for Loop<'a, ChoreoLS, Target, TransportLS, B, V, QSSubsetL, QS, FOC> { - type L = L; + type L = ChoreoLS; type QS = QS; fn f( &self, @@ -871,7 +904,7 @@ where } } let values = QS::foldr( - Loop:: { + Loop:: { phantom: PhantomData, op, }, @@ -883,27 +916,27 @@ where } } } - let op: EppOp<'a, L, L1, LS, B> = EppOp { - target: PhantomData::, + let op: EppOp<'a, ChoreoLS, Target, TransportLS, B> = EppOp { + target: PhantomData::, transport: &self.transport, locations: self.transport.locations(), - marker: PhantomData::, - projector_location_set: PhantomData::, + marker: PhantomData::, + projector_location_set: PhantomData::, }; choreo.run(&op) } } /// Provides a method to run a choreography without end-point projection. -pub struct Runner { - marker: PhantomData, +pub struct Runner { + marker: PhantomData, } -impl Runner { +impl Runner { /// Constructs a runner. pub fn new() -> Self { Runner { - marker: PhantomData::, + marker: PhantomData::, } } @@ -922,7 +955,10 @@ impl Runner { } /// Runs a choreography directly - pub fn run<'a, V, C: Choreography>(&'a self, choreo: C) -> V { + pub fn run<'a, V, C: Choreography>(&'a self, choreo: C) -> V { + // Note: Technically, the location set of the choreography can be a subset of `RunnerLS`. + // However, by using the same type, the compiler can infer `RunnerLS` for given choreography. + struct RunOp(PhantomData); impl ChoreoOp for RunOp { fn locally( @@ -1037,7 +1073,7 @@ impl Runner { unimplemented!() } } - let op: RunOp = RunOp(PhantomData); + let op: RunOp = RunOp(PhantomData); choreo.run(&op) } } From 92bbce75700490ab85947a54df2dc9e73eb7518b Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Tue, 24 Sep 2024 11:21:34 -0700 Subject: [PATCH 13/42] remove singly located values --- chorus_lib/examples/bookseller2.rs | 23 +-- chorus_lib/examples/locationset-fold.rs | 2 +- chorus_lib/examples/multicast.rs | 4 +- chorus_lib/examples/tic-tac-toe.rs | 7 - chorus_lib/src/core.rs | 191 +++++++++++------------- 5 files changed, 103 insertions(+), 124 deletions(-) diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index 8c7894a..9432a26 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -5,7 +5,10 @@ use std::sync::Arc; use std::thread; use chorus_lib::{ - core::{ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, Projector}, + core::{ + ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, MultiplyLocated, + Projector, + }, transport::local::{LocalTransport, LocalTransportChannelBuilder}, }; use chrono::NaiveDate; @@ -22,22 +25,22 @@ struct Seller; type Inventory = HashMap; trait Decider { - fn new(price: Located) -> Self; + fn new(price: MultiplyLocated) -> Self; } struct OneBuyerDecider { - price: Located, + price: MultiplyLocated, } impl Decider for OneBuyerDecider { - fn new(price: Located) -> Self { + fn new(price: MultiplyLocated) -> Self { Self { price } } } -impl Choreography> for OneBuyerDecider { +impl Choreography> for OneBuyerDecider { type L = LocationSet!(Buyer1, Buyer2); - fn run(self, op: &impl ChoreoOp) -> Located { + fn run(self, op: &impl ChoreoOp) -> MultiplyLocated { let price = op.broadcast(Buyer1, self.price); return op.locally(Buyer1, |_| { const BUYER1_BUDGET: i32 = 100; @@ -47,18 +50,18 @@ impl Choreography> for OneBuyerDecider { } struct TwoBuyerDecider { - price: Located, + price: MultiplyLocated, } impl Decider for TwoBuyerDecider { - fn new(price: Located) -> Self { + fn new(price: MultiplyLocated) -> Self { Self { price } } } -impl Choreography> for TwoBuyerDecider { +impl Choreography> for TwoBuyerDecider { type L = LocationSet!(Buyer1, Buyer2); - fn run(self, op: &impl ChoreoOp) -> Located { + fn run(self, op: &impl ChoreoOp) -> MultiplyLocated { let remaining = op.locally(Buyer1, |un| { const BUYER1_BUDGET: i32 = 100; return un.unwrap(&self.price) - BUYER1_BUDGET; diff --git a/chorus_lib/examples/locationset-fold.rs b/chorus_lib/examples/locationset-fold.rs index 00c1278..b5d63d4 100644 --- a/chorus_lib/examples/locationset-fold.rs +++ b/chorus_lib/examples/locationset-fold.rs @@ -19,7 +19,7 @@ fn main() { fn f( &self, acc: String, - curr: Q, + _curr: Q, ) -> String where Self::QS: Subset, diff --git a/chorus_lib/examples/multicast.rs b/chorus_lib/examples/multicast.rs index da76335..56b657d 100644 --- a/chorus_lib/examples/multicast.rs +++ b/chorus_lib/examples/multicast.rs @@ -36,11 +36,11 @@ impl Choreography for MulticastChoreography { let msg_at_bob_and_carol = op.multicast(MulticastBuilder::new(Alice, msg_at_alice).to(Bob).to(Carol)); op.locally(Bob, |un| { - let msg = un.unwrap2(&msg_at_bob_and_carol); + let msg = un.unwrap(&msg_at_bob_and_carol); println!("Bob received: {}", msg); }); op.locally(Carol, |un| { - let msg = un.unwrap2(&msg_at_bob_and_carol); + let msg = un.unwrap(&msg_at_bob_and_carol); println!("Carol received: {}", msg); }); } diff --git a/chorus_lib/examples/tic-tac-toe.rs b/chorus_lib/examples/tic-tac-toe.rs index 6c441ef..686036a 100644 --- a/chorus_lib/examples/tic-tac-toe.rs +++ b/chorus_lib/examples/tic-tac-toe.rs @@ -110,7 +110,6 @@ struct PlayerX; struct PlayerO; trait Brain { - fn get_player(&self) -> char; fn think(&self, board: &Board) -> Board; } @@ -125,9 +124,6 @@ impl UserBrain { } impl Brain for UserBrain { - fn get_player(&self) -> char { - self.player - } fn think(&self, board: &Board) -> Board { println!("Current board:"); board.draw(); @@ -210,9 +206,6 @@ impl MinimaxBrain { } impl Brain for MinimaxBrain { - fn get_player(&self) -> char { - self.player - } fn think(&self, board: &Board) -> Board { // return the board with the best move board.draw(); diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index e7aef3e..f6831ee 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -49,60 +49,7 @@ impl Superposition for () { } /// Represents a value located at a location. -/// -/// The struct takes two type parameters: `V` and `L1`. -/// -/// - `V` is an actual type of the value. -/// - `L1` is the location of the value. It must satisfy the `ChoreographicLocation` trait. -#[derive(PartialEq)] -pub struct Located -where - L1: ChoreographyLocation, -{ - /// `Some` if it is located at the current location and `None` if it is located at another location. - value: Option, - /// The struct is parametrized by the location (`L1`). - phantom: PhantomData, -} - -impl Located -where - L1: ChoreographyLocation, -{ - /// Constructs a struct located at the current location with value - fn local(value: V) -> Self { - Located { - value: Some(value), - phantom: PhantomData, - } - } -} - -/// If the value implements `Clone`, the located value of the same type also implements `Clone`. -impl Clone for Located -where - L1: ChoreographyLocation, -{ - fn clone(&self) -> Self { - Located { - value: self.value.clone(), - phantom: PhantomData, - } - } -} - -impl Superposition for Located -where - L1: ChoreographyLocation, -{ - /// Constructs a struct located at another location - fn remote() -> Self { - Located { - value: None, - phantom: PhantomData, - } - } -} +pub type Located = MultiplyLocated; /// Represents a value located at multiple locations. pub struct MultiplyLocated @@ -126,6 +73,19 @@ where } } +impl Clone for MultiplyLocated +where + V: Clone, + L: LocationSet, +{ + fn clone(&self) -> Self { + MultiplyLocated { + value: self.value.clone(), + phantom: PhantomData, + } + } +} + impl Superposition for MultiplyLocated where L: LocationSet, @@ -336,12 +296,8 @@ pub struct Unwrapper { } impl Unwrapper { - /// Takes a reference to the located value at the current location and returns its reference - pub fn unwrap<'a, V>(&self, located: &'a Located) -> &'a V { - located.value.as_ref().unwrap() - } /// TODO: documentation - pub fn unwrap2<'a, V, S: LocationSet, Index>(&self, mlv: &'a MultiplyLocated) -> &'a V + pub fn unwrap<'a, V, S: LocationSet, Index>(&self, mlv: &'a MultiplyLocated) -> &'a V where L1: Member, { @@ -410,7 +366,7 @@ pub trait ChoreoOp { &self, location: L1, computation: impl Fn(Unwrapper) -> V, - ) -> Located + ) -> MultiplyLocated where L1: Member; /// Performs a communication between two locations. @@ -418,32 +374,36 @@ pub trait ChoreoOp { /// `comm` sends `data` from `sender` to `receiver`. The `data` must be a `Located` struct at the `sender` location /// and the value type must implement `Portable`. fn comm< + L: LocationSet, Sender: ChoreographyLocation, Receiver: ChoreographyLocation, V: Portable, Index1, Index2, + Index3, >( &self, sender: Sender, receiver: Receiver, - data: &Located, - ) -> Located + data: &MultiplyLocated, + ) -> MultiplyLocated where - Sender: Member, - Receiver: Member; + L: Subset, + Sender: Member, + Receiver: Member; /// Performs a broadcast from a location to all other locations. /// /// `broadcast` broadcasts `data` from `sender` to all other locations. The `data` must be a `Located` struct at the `sender` location. /// The method returns the non-located value. - fn broadcast( + fn broadcast( &self, sender: Sender, - data: Located, + data: MultiplyLocated, ) -> V where - Sender: Member; + L: Subset, + Sender: Member; /// TODO: documentation fn multicast( @@ -621,7 +581,11 @@ where /// Unwraps a located value at the projection target. /// /// Use this method to access the located value returned by a choreography. - pub fn unwrap(&self, located: Located) -> V { + pub fn unwrap(&self, located: MultiplyLocated) -> V + where + L: Subset, + Target: Member, + { located.value.unwrap() } @@ -665,52 +629,63 @@ where &self, _location: L1, computation: impl Fn(Unwrapper) -> V, - ) -> Located { + ) -> MultiplyLocated { if L1::name() == Target::name() { let unwrapper = Unwrapper { phantom: PhantomData, }; let value = computation(unwrapper); - Located::local(value) + MultiplyLocated::local(value) } else { - Located::remote() + MultiplyLocated::remote() } } fn comm< - L1: ChoreographyLocation, - L2: ChoreographyLocation, + L: LocationSet, + Sender: ChoreographyLocation, + Receiver: ChoreographyLocation, V: Portable, Index1, Index2, + Index3, >( &self, - _sender: L1, - _receiver: L2, - data: &Located, - ) -> Located { - if L1::name() == Target::name() && L1::name() == L2::name() { + _sender: Sender, + _receiver: Receiver, + data: &MultiplyLocated, + ) -> MultiplyLocated { + if Sender::name() == Target::name() && Sender::name() == Receiver::name() { let s = serde_json::to_string(data.value.as_ref().unwrap()).unwrap(); - return Located::local(serde_json::from_str(s.as_str()).unwrap()); + return MultiplyLocated::local(serde_json::from_str(s.as_str()).unwrap()); } - if L1::name() == Target::name() { - self.transport - .send(L1::name(), L2::name(), data.value.as_ref().unwrap()); - Located::remote() - } else if L2::name() == Target::name() { - let value = self.transport.receive(L1::name(), L2::name()); - Located::local(value) + if Sender::name() == Target::name() { + self.transport.send( + Sender::name(), + Receiver::name(), + data.value.as_ref().unwrap(), + ); + MultiplyLocated::remote() + } else if Receiver::name() == Target::name() { + let value = self.transport.receive(Sender::name(), Receiver::name()); + MultiplyLocated::local(value) } else { - Located::remote() + MultiplyLocated::remote() } } - fn broadcast( + fn broadcast< + L: LocationSet, + Sender: ChoreographyLocation, + V: Portable, + Index1, + Index2, + >( &self, - _sender: L1, - data: Located, + _sender: Sender, + data: MultiplyLocated, ) -> V { - if L1::name() == Target::name() { + if Sender::name() == Target::name() { for dest in &self.locations { if Target::name() != *dest { self.transport.send( @@ -722,7 +697,7 @@ where } return data.value.unwrap(); } else { - self.transport.receive(L1::name(), &Target::name()) + self.transport.receive(Sender::name(), &Target::name()) } } @@ -965,36 +940,44 @@ impl Runner { &self, _location: L1, computation: impl Fn(Unwrapper) -> V, - ) -> Located { + ) -> MultiplyLocated { let unwrapper = Unwrapper { phantom: PhantomData, }; let value = computation(unwrapper); - Located::local(value) + MultiplyLocated::local(value) } fn comm< - L1: ChoreographyLocation, - L2: ChoreographyLocation, + S: LocationSet, + Sender: ChoreographyLocation, + Receiver: ChoreographyLocation, V: Portable, Index1, Index2, + Index3, >( &self, - _sender: L1, - _receiver: L2, - data: &Located, - ) -> Located { + _sender: Sender, + _receiver: Receiver, + data: &MultiplyLocated, + ) -> MultiplyLocated { // clone the value by encoding and decoding it. Requiring `Clone` could improve the performance but is not necessary. // Also, this is closer to what happens to the value with end-point projection. let s = serde_json::to_string(data.value.as_ref().unwrap()).unwrap(); - Located::local(serde_json::from_str(s.as_str()).unwrap()) + MultiplyLocated::local(serde_json::from_str(s.as_str()).unwrap()) } - fn broadcast( + fn broadcast< + S: LocationSet, + Sender: ChoreographyLocation, + V: Portable, + Index1, + Index2, + >( &self, - _sender: L1, - data: Located, + _sender: Sender, + data: MultiplyLocated, ) -> V { data.value.unwrap() } From 5493fbe81272ebcd12aa795c3cd8a26295dcb6e7 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Wed, 2 Oct 2024 09:43:49 -0700 Subject: [PATCH 14/42] allow enclave to return arbitrary value and make it wrap the value in located value --- chorus_lib/examples/bookseller2.rs | 14 +++++++----- chorus_lib/examples/enclave-mlv.rs | 18 ++++++++++----- chorus_lib/examples/loc-poly.rs | 12 +++++----- chorus_lib/examples/runner.rs | 9 +++----- chorus_lib/src/core.rs | 36 ++++++++++++++++++++++-------- 5 files changed, 57 insertions(+), 32 deletions(-) diff --git a/chorus_lib/examples/bookseller2.rs b/chorus_lib/examples/bookseller2.rs index 9432a26..0f97107 100644 --- a/chorus_lib/examples/bookseller2.rs +++ b/chorus_lib/examples/bookseller2.rs @@ -97,7 +97,7 @@ impl, L = LocationSet!(Buyer1, Buyer2)> + return i32::MAX; }); let price_at_buyer1 = op.comm(Seller, Buyer1, &price_at_seller); - let decision_at_buyer1 = op.enclave(D::new(price_at_buyer1)); + let decision_at_buyer1 = op.enclave(D::new(price_at_buyer1)).flatten(); struct GetDeliveryDateChoreography { inventory: Located, @@ -123,11 +123,13 @@ impl, L = LocationSet!(Buyer1, Buyer2)> + } } - return op.enclave(GetDeliveryDateChoreography { - inventory: self.inventory.clone(), - title_at_seller: title_at_seller.clone(), - decision_at_buyer1, - }); + return op + .enclave(GetDeliveryDateChoreography { + inventory: self.inventory.clone(), + title_at_seller: title_at_seller.clone(), + decision_at_buyer1, + }) + .flatten(); } } diff --git a/chorus_lib/examples/enclave-mlv.rs b/chorus_lib/examples/enclave-mlv.rs index e09078c..6ea0e60 100644 --- a/chorus_lib/examples/enclave-mlv.rs +++ b/chorus_lib/examples/enclave-mlv.rs @@ -39,9 +39,13 @@ impl Choreography for MainChoreography { Choice::Bob } }); - let ChoiceAndQuery(choice, query_at_alice) = op.enclave(ChooseQueryChoreography { + let choice_and_query = op.enclave(ChooseQueryChoreography { alice_choice: choice, }); + let query_at_alice = op.locally(Alice, |un| { + let query = un.unwrap(&choice_and_query); + String::from(un.unwrap(&query.1)) + }); let query_at_carol = op.comm(Alice, Carol, &query_at_alice); let response_at_carol = op.locally(Carol, |un| { let query = un.unwrap(&query_at_carol); @@ -50,11 +54,13 @@ impl Choreography for MainChoreography { return r; }); let response = op.broadcast(Carol, response_at_carol); - op.enclave(TerminalChoreography { choice, response }); + op.enclave(TerminalChoreography { + choice_and_query, + response, + }); } } -#[derive(Superposition)] struct ChoiceAndQuery( MultiplyLocated, Located, @@ -80,15 +86,15 @@ impl Choreography for ChooseQueryChoreography { } struct TerminalChoreography { - choice: MultiplyLocated, + choice_and_query: MultiplyLocated, response: String, } impl Choreography for TerminalChoreography { type L = LocationSet!(Alice, Bob); fn run(self, op: &impl ChoreoOp) { - let choice = op.naked(self.choice); - match choice { + let ChoiceAndQuery(choice, _) = op.naked(self.choice_and_query); + match op.naked(choice) { Choice::Alice => { op.locally(Alice, |_| { println!("Alice received response: {}", self.response); diff --git a/chorus_lib/examples/loc-poly.rs b/chorus_lib/examples/loc-poly.rs index accaadb..b6d3e67 100644 --- a/chorus_lib/examples/loc-poly.rs +++ b/chorus_lib/examples/loc-poly.rs @@ -47,11 +47,13 @@ impl Choreography> for MainChoreography { data: v1, }); let v2 = op.locally(Bob, |un| un.unwrap(&v2) + 10); - return op.enclave(CommAndPrint { - sender: Bob, - receiver: Alice, - data: v2, - }); + return op + .enclave(CommAndPrint { + sender: Bob, + receiver: Alice, + data: v2, + }) + .flatten(); } } diff --git a/chorus_lib/examples/runner.rs b/chorus_lib/examples/runner.rs index 0692d97..fbffbc9 100644 --- a/chorus_lib/examples/runner.rs +++ b/chorus_lib/examples/runner.rs @@ -53,17 +53,14 @@ impl Choreography for MainChoreography { fn run(self, op: &impl ChoreoOp) { let x_at_alice = op.locally(Alice, |_| get_random_number()); let x_at_bob = op.comm(Alice, Bob, &x_at_alice); - let BobCarolResult { - is_even_at_bob, - is_even_at_carol, - } = op.enclave(BobCarolChoreography { x_at_bob }); + let result = op.enclave(BobCarolChoreography { x_at_bob }); op.locally(Bob, |un| { - let is_even = un.unwrap(&is_even_at_bob); + let is_even = un.unwrap(&un.unwrap(&result).is_even_at_bob); assert!(is_even); println!("Bob: x is even: {}", is_even); }); op.locally(Carol, |un| { - let is_even = un.unwrap(&is_even_at_carol); + let is_even = un.unwrap(&un.unwrap(&result).is_even_at_carol); assert!(is_even); println!("Carol: x is even: {}", is_even); }); diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index f6831ee..3415bdf 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -73,6 +73,24 @@ where } } +impl MultiplyLocated, LS2> +where + LS1: LocationSet, + LS2: LocationSet, +{ + /// Flattens a located value located at multiple locations. + pub fn flatten(self) -> MultiplyLocated + where + LS1: Subset, + { + let value = self.value.unwrap().value; + MultiplyLocated { + value, + phantom: PhantomData, + } + } +} + impl Clone for MultiplyLocated where V: Clone, @@ -428,10 +446,10 @@ pub trait ChoreoOp { M: LocationSet + Subset; /// Calls a choreography on a subset of locations. - fn enclave, Index>( + fn enclave, Index>( &self, choreo: C, - ) -> R + ) -> MultiplyLocated where S: Subset; @@ -757,10 +775,10 @@ where choreo.run(&op) } - fn enclave, Index>( + fn enclave, Index>( &self, choreo: C, - ) -> R { + ) -> MultiplyLocated { let locs_vec = S::to_string_list(); for location in &locs_vec { @@ -772,10 +790,10 @@ where marker: PhantomData::, projector_location_set: PhantomData::, }; - return choreo.run(&op); + return MultiplyLocated::local(choreo.run(&op)); } } - R::remote() + MultiplyLocated::remote() } fn parallel( @@ -1008,12 +1026,12 @@ impl Runner { choreo.run(&op) } - fn enclave, Index>( + fn enclave, Index>( &self, choreo: C, - ) -> R { + ) -> MultiplyLocated { let op = RunOp::(PhantomData); - choreo.run(&op) + MultiplyLocated::local(choreo.run(&op)) } fn parallel( From f6203933af741f44fafc986ed834495dcdf4181a Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Tue, 8 Oct 2024 17:20:17 -0700 Subject: [PATCH 15/42] draft fanin --- chorus_lib/src/core.rs | 201 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 198 insertions(+), 3 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 3415bdf..b293981 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -117,6 +117,15 @@ where } } +/// Represents a mapping from location names to values +pub struct Quire +where + L: LocationSet, +{ + value: HashMap, + phantom: PhantomData, +} + /// Represents possibly different values located at multiple locations #[derive(Debug)] pub struct Faceted @@ -481,9 +490,33 @@ pub trait ChoreoOp { where QS: Subset, QS: LocationSetFoldable; + + /// Performs fanin computation. + fn fanin< + // return value type + V, + // locations looping over + QS: LocationSet, + // Recipient locations + RS: LocationSet, + // FanIn Choreography over L iterating over QS returning V + FIC: FanInChoreography, + // Proof that QS is a subset of L + QSSubsetL, + RSSubsetL, + QSFoldable, + >( + &self, + locations: QS, + c: FIC, + ) -> MultiplyLocated, RS> + where + QS: Subset, + RS: Subset, + QS: LocationSetFoldable; } -/// TODO: documentation +/// Special choreography for fanout pub trait FanOutChoreography { /// All locations involved in the choreography type L: LocationSet; @@ -502,6 +535,28 @@ pub trait FanOutChoreography { Q: Member; } +/// Special choreography for fanin +pub trait FanInChoreography { + /// All locations involved in the choreography + type L: LocationSet; + /// Locations looping over + type QS: LocationSet; + /// Recipient locations + type RS: LocationSet; + /// Constructor + fn new() -> Self; + /// run a choreography + fn run( + self, + op: &impl ChoreoOp, + ) -> MultiplyLocated + where + Self::QS: Subset, + Self::RS: Subset, + Q: Member, + Q: Member; +} + /// Represents a choreography. /// /// The trait for defining a choreography. It should be implemented for a struct that represents a choreography. @@ -878,7 +933,7 @@ where fn f( &self, mut acc: HashMap, - curr: Q, + _: Q, ) -> HashMap where Self::QS: Subset, @@ -908,6 +963,120 @@ where phantom: PhantomData, } } + fn fanin< + // return value type + V, + // locations looping over + QS: LocationSet, + // Recipient locations + RS: LocationSet, + // FanIn Choreography over L iterating over QS returning V + FIC: FanInChoreography, + // Proof that QS is a subset of L + QSSubsetL, + RSSubsetL, + QSFoldable, + >( + &self, + locations: QS, + c: FIC, + ) -> MultiplyLocated, RS> + where + QS: Subset, + RS: Subset, + QS: LocationSetFoldable, + { + let op: EppOp = EppOp { + target: PhantomData::, + transport: self.transport, + locations: self.transport.locations(), + marker: PhantomData::, + projector_location_set: PhantomData::, + }; + + struct Loop< + 'a, + ChoreoLS: LocationSet, + Target: ChoreographyLocation, + TransportLS: LocationSet, + B: Transport, + V, + QSSubsetL, + QS: LocationSet + Subset, + RSSubsetL, + RS: LocationSet + Subset, + FIC: FanInChoreography, + > { + phantom: PhantomData<(V, QS, QSSubsetL, RS, RSSubsetL, FIC)>, + op: EppOp<'a, ChoreoLS, Target, TransportLS, B>, + } + + impl< + 'a, + ChoreoLS: LocationSet, + Target: ChoreographyLocation, + TransportLS: LocationSet, + B: Transport, + V, + QSSubsetL, + QS: LocationSet + Subset, + RSSubsetL, + RS: LocationSet + Subset, + FIC: FanInChoreography, + > LocationSetFolder> + for Loop< + 'a, + ChoreoLS, + Target, + TransportLS, + B, + V, + QSSubsetL, + QS, + RSSubsetL, + RS, + FIC, + > + { + type L = ChoreoLS; + type QS = QS; + + fn f( + &self, + mut acc: HashMap, + _: Q, + ) -> HashMap + where + Self::QS: Subset, + Q: Member, + Q: Member, + { + let c = FIC::new(); + let v = c.run::(&self.op); + // if the target is in RS, `v` has a value (`Some`) + match v.value { + Some(value) => { + acc.insert(String::from(Q::name()), value); + } + None => {} + } + acc + } + } + + let values = QS::foldr( + Loop:: { + phantom: PhantomData, + op, + }, + HashMap::new(), + ); + + MultiplyLocated::, RS>::local(Quire { + value: values, + phantom: PhantomData, + }) + } } let op: EppOp<'a, ChoreoLS, Target, TransportLS, B> = EppOp { target: PhantomData::, @@ -1071,7 +1240,33 @@ impl Runner { QS: Subset, QS: LocationSetFoldable, { - unimplemented!() + todo!() + } + + fn fanin< + // return value type + V, + // locations looping over + QS: LocationSet, + // Recipient locations + RS: LocationSet, + // FanIn Choreography over L iterating over QS returning V + FIC: FanInChoreography, + // Proof that QS is a subset of L + QSSubsetL, + RSSubsetL, + QSFoldable, + >( + &self, + locations: QS, + c: FIC, + ) -> MultiplyLocated, RS> + where + QS: Subset, + RS: Subset, + QS: LocationSetFoldable, + { + todo!() } } let op: RunOp = RunOp(PhantomData); From f2c4b1904c9b8fd70638518ac2a3ff043bd7eb37 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Tue, 15 Oct 2024 15:49:13 -0700 Subject: [PATCH 16/42] almost finish cardgame --- chorus_lib/examples/cardgame.rs | 298 +++++++++++++++++++++++++++++++ chorus_lib/examples/fanout.rs | 7 +- chorus_lib/examples/multicast.rs | 2 +- chorus_lib/src/core.rs | 97 ++++++---- 4 files changed, 361 insertions(+), 43 deletions(-) create mode 100644 chorus_lib/examples/cardgame.rs diff --git a/chorus_lib/examples/cardgame.rs b/chorus_lib/examples/cardgame.rs new file mode 100644 index 0000000..c070a1c --- /dev/null +++ b/chorus_lib/examples/cardgame.rs @@ -0,0 +1,298 @@ +extern crate chorus_lib; + +use std::marker::PhantomData; + +use chorus_lib::core::{ + ChoreoOp, Choreography, ChoreographyLocation, Faceted, FanInChoreography, FanOutChoreography, + HCons, Located, LocationSet, LocationSetFoldable, Member, MultiplyLocated, Projector, Quire, + Subset, +}; +use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; + +#[derive(ChoreographyLocation)] +struct Dealer; + +#[derive(ChoreographyLocation)] +struct Player1; +#[derive(ChoreographyLocation)] +struct Player2; +#[derive(ChoreographyLocation)] +struct Player3; + +struct Game< + Players: LocationSet + + Subset, PlayersSubsetAll> + + LocationSetFoldable, Players, PlayersFoldable>, + PlayersSubsetAll, + PlayersFoldable, +> { + phantom: PhantomData<(Players, PlayersSubsetAll, PlayersFoldable)>, +} + +impl< + Players: LocationSet + + Subset, PlayersSubsetAll> + + LocationSetFoldable, Players, PlayersFoldable>, + PlayersSubsetAll, + PlayersFoldable, + > Game +{ + fn new(_: Players) -> Self + where + Players: Subset, PlayerSubsetAll>, + { + Self { + phantom: PhantomData {}, + } + } +} + +impl< + Players: LocationSet + + Subset, PlayersSubsetAll> + + LocationSetFoldable, Players, PlayersFoldable>, + PlayersSubsetAll, + PlayersFoldable, + > Choreography for Game +{ + type L = HCons; + + fn run(self, op: &impl ChoreoOp) -> () { + struct Collect(PhantomData); + impl FanOutChoreography for Collect { + type L = HCons; + type QS = Players; + fn run( + &self, + op: &impl ChoreoOp, + ) -> Located + where + Self::QS: Subset, + Q: Member, + Q: Member, + { + let card1 = op.locally(Dealer, |_| { + println!("Enter the first card for {:?}", Q::name()); + let mut input = String::new(); + std::io::stdin() + .read_line(&mut input) + .expect("Failed to read line"); + input.trim().parse::().expect("Failed to parse input") + }); + op.comm(Dealer, Q::new(), &card1) + } + } + let hand1 = op.fanout(Players::new(), Collect(PhantomData)); + + struct Gather< + 'a, + Players: LocationSet + Subset, PlayersSubset>, + PlayersSubset, + > { + hand1: &'a Faceted, + phantom: PhantomData, + } + impl< + 'a, + Players: LocationSet + Subset, PlayersSubset>, + PlayersSubset, + > FanInChoreography for Gather<'a, Players, PlayersSubset> + { + type L = HCons; + type QS = Players; + type RS = Players; + fn run( + &self, + op: &impl ChoreoOp, + ) -> MultiplyLocated + where + Self::QS: Subset, + Self::RS: Subset, + Q: Member, + Q: Member, + { + let x = op.locally(Q::new(), |un| *un.unwrap3(&self.hand1)); + let x = op.multicast::( + Q::new(), + ::new(), + &x, + ); + x + } + } + let on_the_table = op.fanin( + Players::new(), + Gather { + hand1: &hand1, + phantom: PhantomData, + }, + ); + + struct Choice<'a, Players: LocationSet> { + hand1: &'a Faceted, + on_the_table: &'a MultiplyLocated, Players>, + } + impl<'a, Players: LocationSet> FanOutChoreography for Choice<'a, Players> { + type L = HCons; + type QS = Players; + + fn run( + &self, + op: &impl ChoreoOp, + ) -> Located + where + Self::QS: Subset, + Q: Member, + Q: Member, + { + op.locally(Q::new(), |un| { + let hand1 = *un.unwrap3(&self.hand1); + let on_the_table = un.unwrap(&self.on_the_table); + println!("My first card is: {}", hand1); + println!("On the table: {:?}", on_the_table); + println!("I'll ask for another? [True/False]"); + let mut input = String::new(); + std::io::stdin() + .read_line(&mut input) + .expect("Failed to read line"); + let input = input.trim(); + if input == "True" { + true + } else { + false + } + }) + } + } + + let wants_next_card = op.fanout( + Players::new(), + Choice { + hand1: &hand1, + on_the_table: &on_the_table, + }, + ); + + struct Collect2<'a, Players: LocationSet> { + hand1: &'a Faceted, + wants_next_card: &'a Faceted, + } + impl<'a, Players: LocationSet> FanOutChoreography> for Collect2<'a, Players> { + type L = HCons; + type QS = Players; + fn run( + &self, + op: &impl ChoreoOp, + ) -> Located, Q> + where + Self::QS: Subset, + Q: Member, + Q: Member, + { + // TODO: enclave + let choice_at_q = op.locally(Q::new(), |un| *un.unwrap3(self.wants_next_card)); + let choice = op.broadcast(Q::new(), choice_at_q); + if choice { + let card2 = op.locally(Dealer, |_| { + println!("Player {:?} wants another card", Q::name()); + let mut input = String::new(); + std::io::stdin() + .read_line(&mut input) + .expect("Failed to read line"); + input.trim().parse::().expect("Failed to parse input") + }); + let card2 = op.comm(Dealer, Q::new(), &card2); + op.locally(Q::new(), |un| { + vec![*un.unwrap3(self.hand1), *un.unwrap(&card2)] + }) + } else { + op.locally(Q::new(), |un| vec![*un.unwrap3(self.hand1)]) + } + } + } + let hand2 = op.fanout( + Players::new(), + Collect2 { + hand1: &hand1, + wants_next_card: &wants_next_card, + }, + ); + let tbl_card = op.locally(Dealer, |_| { + println!("Enter a single card for everyone "); + let mut input = String::new(); + std::io::stdin() + .read_line(&mut input) + .expect("Failed to read line"); + input.trim().parse::().expect("Failed to parse input") + }); + let table_card = op.broadcast(Dealer, tbl_card); + + struct Outcome<'a, Players: LocationSet> { + hand2: &'a Faceted, Players>, + table_card: i32, + } + impl<'a, Players: LocationSet> FanOutChoreography<()> for Outcome<'a, Players> { + type L = HCons; + type QS = Players; + fn run( + &self, + op: &impl ChoreoOp, + ) -> Located<(), Q> + where + Self::QS: Subset, + Q: Member, + Q: Member, + { + op.locally(Q::new(), |un| { + let mut hand2 = un.unwrap3(self.hand2).clone(); + hand2.push(self.table_card); + println!("Final hands: {:?}", hand2); + let sum: i32 = hand2.iter().sum(); + println!("My win result: {}", sum > 19); + return (); + }) + } + } + + op.fanout( + Players::new(), + Outcome { + hand2: &hand2, + table_card, + }, + ); + } +} + +fn main() { + let transport_channel = LocalTransportChannelBuilder::new() + .with(Dealer) + .with(Player1) + .with(Player2) + .build(); + + type Players = LocationSet!(Player1, Player2); + + let transport_dealer = LocalTransport::new(Dealer, transport_channel.clone()); + let transport_player1 = LocalTransport::new(Player1, transport_channel.clone()); + let transport_player2 = LocalTransport::new(Player2, transport_channel.clone()); + + let dealer_projector = Projector::new(Dealer, transport_dealer); + let player1_projector = Projector::new(Player1, transport_player1); + let player2_projector = Projector::new(Player2, transport_player2); + + let mut handles = Vec::new(); + handles.push(std::thread::spawn(move || { + dealer_projector.epp_and_run(Game::new(Players::new())); + })); + handles.push(std::thread::spawn(move || { + player1_projector.epp_and_run(Game::new(Players::new())); + })); + handles.push(std::thread::spawn(move || { + player2_projector.epp_and_run(Game::new(Players::new())); + })); + + for handle in handles { + handle.join().unwrap(); + } +} diff --git a/chorus_lib/examples/fanout.rs b/chorus_lib/examples/fanout.rs index 4a34753..9ac0caa 100644 --- a/chorus_lib/examples/fanout.rs +++ b/chorus_lib/examples/fanout.rs @@ -47,13 +47,8 @@ where { type L = L; type QS = QS; - fn new() -> Self { - FanOut { - phantom: PhantomData, - } - } fn run( - self, + &self, op: &impl ChoreoOp, ) -> Located where diff --git a/chorus_lib/examples/multicast.rs b/chorus_lib/examples/multicast.rs index 56b657d..0a514a7 100644 --- a/chorus_lib/examples/multicast.rs +++ b/chorus_lib/examples/multicast.rs @@ -34,7 +34,7 @@ impl Choreography for MulticastChoreography { "Hello from Alice!".to_string() }); let msg_at_bob_and_carol = - op.multicast(MulticastBuilder::new(Alice, msg_at_alice).to(Bob).to(Carol)); + op.multicast(Alice, ::new(), &msg_at_alice); op.locally(Bob, |un| { let msg = un.unwrap(&msg_at_bob_and_carol); println!("Bob received: {}", msg); diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index b293981..face503 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -2,7 +2,7 @@ //! //! This module provides core choreography constructs, such as `Choreography`, `Located`, and `Projector`. -use std::{collections::HashMap, marker::PhantomData}; +use std::{collections::HashMap, fmt::Debug, marker::PhantomData}; use serde::de::DeserializeOwned; // re-export so that users can use derive macros without importing serde @@ -126,6 +126,16 @@ where phantom: PhantomData, } +impl Debug for Quire +where + L: LocationSet, + V: Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_map().entries(self.value.iter()).finish() + } +} + /// Represents possibly different values located at multiple locations #[derive(Debug)] pub struct Faceted @@ -191,7 +201,7 @@ impl< HeadMemberL, HeadMemberQS, ITail, - > LocationSetFoldable>>> + > LocationSetFoldable for HCons where QS: Subset, @@ -267,6 +277,8 @@ pub use __ChoRus_Internal_LocationSet as LocationSet; /// Marker #[doc(hidden)] pub struct Here; +#[doc(hidden)] +pub struct Here2; /// Marker #[doc(hidden)] pub struct There(Index); @@ -303,17 +315,16 @@ where /// /// It takes two type parameters `L` and `Index`. `L` is a location set and `Index` is some type that is inferred by the compiler. /// If a location set `M` is a subset of `L`, then there exists a type `Index` such that `M` implements `Subset`. -pub trait Subset {} +pub trait Subset {} -// Base case: HNil is a subset of any set -impl Subset for HNil {} +// Base case: `HNil` is a subset of any collection +impl Subset for HNil {} -// Recursive case -impl Subset> - for HCons +// Recursive case: `Head` is a `Member` and `Tail` is a `Subset` +impl Subset for HCons where - Head: Member, - Tail: Subset, + Head: Member, + Tail: Subset, { } @@ -435,7 +446,9 @@ pub trait ChoreoOp { /// TODO: documentation fn multicast( &self, - builder: MulticastBuilder, + src: Sender, + destination: D, + data: &MultiplyLocated, ) -> MultiplyLocated where Sender: Member, @@ -523,10 +536,8 @@ pub trait FanOutChoreography { /// Locations looping over type QS: LocationSet; /// TODO: documentation - fn new() -> Self; - /// TODO: documentation fn run( - self, + &self, op: &impl ChoreoOp, ) -> Located where @@ -543,11 +554,9 @@ pub trait FanInChoreography { type QS: LocationSet; /// Recipient locations type RS: LocationSet; - /// Constructor - fn new() -> Self; /// run a choreography fn run( - self, + &self, op: &impl ChoreoOp, ) -> MultiplyLocated where @@ -774,24 +783,30 @@ where } } - fn multicast( + fn multicast< + Sender: ChoreographyLocation, + V: Portable, + D: LocationSet, + Index1, + Index2, + >( &self, - builder: MulticastBuilder, + src: Sender, + destination: D, + data: &MultiplyLocated, ) -> MultiplyLocated { - if L1::name() == Target::name() { + if Sender::name() == Target::name() { for dest in D::to_string_list() { if Target::name() != dest { self.transport.send( &Target::name(), dest, - builder.data.value.as_ref().unwrap(), + data.value.as_ref().unwrap(), ); } } - return MultiplyLocated { - value: builder.data.value, - phantom: PhantomData, - }; + let s = serde_json::to_string(data.value.as_ref().unwrap()).unwrap(); + return MultiplyLocated::local(serde_json::from_str(s.as_str()).unwrap()); } else { let mut is_receiver = false; for dest in D::to_string_list() { @@ -800,7 +815,7 @@ where } } if is_receiver { - let v = self.transport.receive(L1::name(), Target::name()); + let v = self.transport.receive(Sender::name(), Target::name()); return MultiplyLocated::local(v); } else { return MultiplyLocated::remote(); @@ -913,6 +928,7 @@ where > { phantom: PhantomData<(V, QS, QSSubsetL, FOC)>, op: EppOp<'a, ChoreoLS, Target, TransportLS, B>, + foc: FOC, } impl< @@ -940,8 +956,7 @@ where Q: Member, Q: Member, { - let c = FOC::new(); - let v = c.run::(&self.op); + let v = self.foc.run::(&self.op); match v.value { Some(value) => { acc.insert(String::from(Q::name()), value); @@ -955,6 +970,7 @@ where Loop:: { phantom: PhantomData, op, + foc: c, }, values, ); @@ -1009,6 +1025,7 @@ where > { phantom: PhantomData<(V, QS, QSSubsetL, RS, RSSubsetL, FIC)>, op: EppOp<'a, ChoreoLS, Target, TransportLS, B>, + fic: FIC, } impl< @@ -1051,8 +1068,9 @@ where Q: Member, Q: Member, { - let c = FIC::new(); - let v = c.run::(&self.op); + let v = self + .fic + .run::(&self.op); // if the target is in RS, `v` has a value (`Some`) match v.value { Some(value) => { @@ -1068,6 +1086,7 @@ where Loop:: { phantom: PhantomData, op, + fic: c, }, HashMap::new(), ); @@ -1169,14 +1188,20 @@ impl Runner { data.value.unwrap() } - fn multicast( + fn multicast< + Sender: ChoreographyLocation, + V: Portable, + D: LocationSet, + Index1, + Index2, + >( &self, - builder: MulticastBuilder, + src: Sender, + destination: D, + data: &MultiplyLocated, ) -> MultiplyLocated { - MultiplyLocated { - value: builder.data.value, - phantom: PhantomData, - } + let s = serde_json::to_string(data.value.as_ref().unwrap()).unwrap(); + return MultiplyLocated::local(serde_json::from_str(s.as_str()).unwrap()); } fn naked(&self, data: MultiplyLocated) -> V { From a5f3c7ea2a40433a252206a086870986318041a8 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Tue, 15 Oct 2024 16:04:27 -0700 Subject: [PATCH 17/42] this type checks but crashes at runtime :sob: --- chorus_lib/examples/cardgame.rs | 50 ++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/chorus_lib/examples/cardgame.rs b/chorus_lib/examples/cardgame.rs index c070a1c..b355b71 100644 --- a/chorus_lib/examples/cardgame.rs +++ b/chorus_lib/examples/cardgame.rs @@ -190,24 +190,40 @@ impl< Q: Member, { // TODO: enclave - let choice_at_q = op.locally(Q::new(), |un| *un.unwrap3(self.wants_next_card)); - let choice = op.broadcast(Q::new(), choice_at_q); - if choice { - let card2 = op.locally(Dealer, |_| { - println!("Player {:?} wants another card", Q::name()); - let mut input = String::new(); - std::io::stdin() - .read_line(&mut input) - .expect("Failed to read line"); - input.trim().parse::().expect("Failed to parse input") - }); - let card2 = op.comm(Dealer, Q::new(), &card2); - op.locally(Q::new(), |un| { - vec![*un.unwrap3(self.hand1), *un.unwrap(&card2)] - }) - } else { - op.locally(Q::new(), |un| vec![*un.unwrap3(self.hand1)]) + struct Enclave { + hand1: Located, + wants_next_card: Located, } + impl Choreography, Player>> for Enclave { + type L = LocationSet!(Dealer, Player); + + fn run(self, op: &impl ChoreoOp) -> Located, Player> { + let choice = op.broadcast(Player::new(), self.wants_next_card.clone()); + if choice { + let card2 = op.locally(Dealer, |_| { + println!("Player {:?} wants another card", Player::name()); + let mut input = String::new(); + std::io::stdin() + .read_line(&mut input) + .expect("Failed to read line"); + input.trim().parse::().expect("Failed to parse input") + }); + let card2 = op.comm(Dealer, Player::new(), &card2); + op.locally(Player::new(), |un| { + vec![*un.unwrap(&self.hand1), *un.unwrap(&card2)] + }) + } else { + op.locally(Player::new(), |un| vec![*un.unwrap(&self.hand1)]) + } + } + } + let hand1 = op.locally(Q::new(), |un| *un.unwrap3(self.hand1)); + let wants_next_card = op.locally(Q::new(), |un| *un.unwrap3(self.wants_next_card)); + op.enclave(Enclave:: { + hand1, + wants_next_card, + }) + .flatten() } } let hand2 = op.fanout( From df5c39b29db3a93caf255f7c17358a4c9e09fb80 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Tue, 15 Oct 2024 20:15:16 -0700 Subject: [PATCH 18/42] fix flatten --- chorus_lib/examples/cardgame.rs | 29 +++++++++++++---------------- chorus_lib/src/core.rs | 2 +- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/chorus_lib/examples/cardgame.rs b/chorus_lib/examples/cardgame.rs index b355b71..c4d3701 100644 --- a/chorus_lib/examples/cardgame.rs +++ b/chorus_lib/examples/cardgame.rs @@ -19,6 +19,14 @@ struct Player2; #[derive(ChoreographyLocation)] struct Player3; +fn read_i32() -> i32 { + let mut input = String::new(); + std::io::stdin() + .read_line(&mut input) + .expect("Failed to read line"); + input.trim().parse::().expect("Failed to parse input") +} + struct Game< Players: LocationSet + Subset, PlayersSubsetAll> @@ -73,11 +81,7 @@ impl< { let card1 = op.locally(Dealer, |_| { println!("Enter the first card for {:?}", Q::name()); - let mut input = String::new(); - std::io::stdin() - .read_line(&mut input) - .expect("Failed to read line"); - input.trim().parse::().expect("Failed to parse input") + read_i32() }); op.comm(Dealer, Q::new(), &card1) } @@ -202,11 +206,8 @@ impl< if choice { let card2 = op.locally(Dealer, |_| { println!("Player {:?} wants another card", Player::name()); - let mut input = String::new(); - std::io::stdin() - .read_line(&mut input) - .expect("Failed to read line"); - input.trim().parse::().expect("Failed to parse input") + println!("Enter the second card for {:?}", Player::name()); + read_i32() }); let card2 = op.comm(Dealer, Player::new(), &card2); op.locally(Player::new(), |un| { @@ -235,11 +236,7 @@ impl< ); let tbl_card = op.locally(Dealer, |_| { println!("Enter a single card for everyone "); - let mut input = String::new(); - std::io::stdin() - .read_line(&mut input) - .expect("Failed to read line"); - input.trim().parse::().expect("Failed to parse input") + read_i32() }); let table_card = op.broadcast(Dealer, tbl_card); @@ -264,7 +261,7 @@ impl< hand2.push(self.table_card); println!("Final hands: {:?}", hand2); let sum: i32 = hand2.iter().sum(); - println!("My win result: {}", sum > 19); + println!("My win result: {}", sum % 21 > 19); return (); }) } diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index face503..84f6449 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -83,7 +83,7 @@ where where LS1: Subset, { - let value = self.value.unwrap().value; + let value = self.value.map(|x| x.value).flatten(); MultiplyLocated { value, phantom: PhantomData, From 4083a07c380f3d9f7e9909b26fcd3601aecd18d7 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Wed, 16 Oct 2024 17:11:48 -0700 Subject: [PATCH 19/42] delete outdated document --- chorus_book/src/guide-enclave.md | 64 +------------------------------- 1 file changed, 1 insertion(+), 63 deletions(-) diff --git a/chorus_book/src/guide-enclave.md b/chorus_book/src/guide-enclave.md index 2ff895c..f14f9b7 100644 --- a/chorus_book/src/guide-enclave.md +++ b/chorus_book/src/guide-enclave.md @@ -112,66 +112,4 @@ impl Choreography for MainChoreography { } ``` -## Returning Values from Enclave - -Just like the `call` operator, the `enclave` operator can return a value. However, the type of the returned value must implement the `Superposition` trait. `Superposition` provides a way for ChoRus to construct a value on locations that are not specified in the `enclave` operator. - -In general, `Superposition` is either a located value or a struct consisting only of located values. The `Located` struct implements the `Superposition` trait, so you can return located values without any code. If you wish to return a struct of located values, you need to derive the `Superposition` trait using the derive macro. - -```rust -{{#include ./header.txt}} -# fn get_random_number() -> u32 { -# 42 // for presentation purpose -# } -# -#[derive(Superposition)] -struct BobCarolResult { - is_even_at_bob: Located, - is_even_at_carol: Located, -} - -struct BobCarolChoreography { - x_at_bob: Located, -}; - -impl Choreography for BobCarolChoreography { - type L = LocationSet!(Bob, Carol); - fn run(self, op: &impl ChoreoOp) -> BobCarolResult { - let is_even_at_bob: Located = op.locally(Bob, |un| { - let x = un.unwrap(&self.x_at_bob); - x % 2 == 0 - }); - let is_even: bool = op.broadcast(Bob, is_even_at_bob.clone()); - if is_even { - let x_at_carol = op.comm(Bob, Carol, &self.x_at_bob); - op.locally(Carol, |un| { - let x = un.unwrap(&x_at_carol); - println!("x is even: {}", x); - }); - } - BobCarolResult { - is_even_at_bob, - is_even_at_carol: op.locally(Carol, |_| is_even), - } - } -} - -struct MainChoreography; - -impl Choreography for MainChoreography { - type L = LocationSet!(Alice, Bob, Carol); - fn run(self, op: &impl ChoreoOp) { - let x_at_alice = op.locally(Alice, |_| { - get_random_number() - }); - let x_at_bob = op.comm(Alice, Bob, &x_at_alice); - let BobCarolResult { - is_even_at_bob, - is_even_at_carol, - } = op.enclave(BobCarolChoreography { - x_at_bob, - }); - // can access is_even_at_bob and is_even_at_carol using `locally` on Bob and Carol - } -} -``` + From ca7292cc5436ab788f052f1009e5742dab3a4869 Mon Sep 17 00:00:00 2001 From: Mako Bates <33739499+ShapeOfMatter@users.noreply.github.com> Date: Thu, 17 Oct 2024 11:13:28 -0400 Subject: [PATCH 20/42] Linting --- chorus_lib/examples/enclave-mlv.rs | 2 +- chorus_lib/examples/multicast.rs | 2 +- chorus_lib/src/core.rs | 28 ++++++++++++++-------------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/chorus_lib/examples/enclave-mlv.rs b/chorus_lib/examples/enclave-mlv.rs index 6ea0e60..408c337 100644 --- a/chorus_lib/examples/enclave-mlv.rs +++ b/chorus_lib/examples/enclave-mlv.rs @@ -1,7 +1,7 @@ use chorus_lib::{ core::{ ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, MultiplyLocated, - Projector, Superposition, + Projector, }, transport::local::{LocalTransport, LocalTransportChannelBuilder}, }; diff --git a/chorus_lib/examples/multicast.rs b/chorus_lib/examples/multicast.rs index 0a514a7..a376f7b 100644 --- a/chorus_lib/examples/multicast.rs +++ b/chorus_lib/examples/multicast.rs @@ -3,7 +3,7 @@ extern crate chorus_lib; use std::thread; use chorus_lib::core::{ - ChoreoOp, Choreography, ChoreographyLocation, LocationSet, MulticastBuilder, Projector, + ChoreoOp, Choreography, ChoreographyLocation, LocationSet, Projector, }; use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 84f6449..279d094 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -187,7 +187,7 @@ pub trait LocationSetFoldable { } impl LocationSetFoldable for HNil { - fn foldr>(f: F, acc: B) -> B { + fn foldr>(_f: F, acc: B) -> B { acc } } @@ -361,7 +361,7 @@ impl MulticastBuilder) -> MulticastBuilder { + pub fn new(_source: S, data: Located) -> MulticastBuilder { MulticastBuilder { data, phantom: PhantomData, @@ -791,8 +791,8 @@ where Index2, >( &self, - src: Sender, - destination: D, + _src: Sender, + _destination: D, data: &MultiplyLocated, ) -> MultiplyLocated { if Sender::name() == Target::name() { @@ -868,7 +868,7 @@ where fn parallel( &self, - locations: S, + _locations: S, computation: impl Fn() -> V, // TODO: add unwrapper for S ) -> Faceted where @@ -899,7 +899,7 @@ where QSFoldable, >( &self, - locations: QS, + _locations: QS, c: FOC, ) -> Faceted where @@ -994,7 +994,7 @@ where QSFoldable, >( &self, - locations: QS, + _locations: QS, c: FIC, ) -> MultiplyLocated, RS> where @@ -1196,8 +1196,8 @@ impl Runner { Index2, >( &self, - src: Sender, - destination: D, + _src: Sender, + _destination: D, data: &MultiplyLocated, ) -> MultiplyLocated { let s = serde_json::to_string(data.value.as_ref().unwrap()).unwrap(); @@ -1230,7 +1230,7 @@ impl Runner { fn parallel( &self, - locations: S, + _locations: S, computation: impl Fn() -> V, // TODO: add unwrapper for S ) -> Faceted where @@ -1258,8 +1258,8 @@ impl Runner { QSFoldable, >( &self, - locations: QS, - c: FOC, + _locations: QS, + _c: FOC, ) -> Faceted where QS: Subset, @@ -1283,8 +1283,8 @@ impl Runner { QSFoldable, >( &self, - locations: QS, - c: FIC, + _locations: QS, + _c: FIC, ) -> MultiplyLocated, RS> where QS: Subset, From c7ff7ea64e77e9a58e2ec26b99ca2e76908b520a Mon Sep 17 00:00:00 2001 From: Mako Bates <33739499+ShapeOfMatter@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:49:14 -0400 Subject: [PATCH 21/42] it runs --- chorus_lib/tests/booksellers.rs | 186 ++++++++++++++++---------------- 1 file changed, 91 insertions(+), 95 deletions(-) diff --git a/chorus_lib/tests/booksellers.rs b/chorus_lib/tests/booksellers.rs index ee495ef..e148583 100644 --- a/chorus_lib/tests/booksellers.rs +++ b/chorus_lib/tests/booksellers.rs @@ -1,6 +1,7 @@ extern crate chorus_lib; use std::collections::HashMap; +use std::marker::PhantomData; use std::sync::Arc; use std::thread; @@ -24,54 +25,21 @@ type Title = String; type Inventory = HashMap; -type Decider = fn(Located, Buyer1>) -> Box, L = LocationSet!(Buyer1, Buyer2)>>; -fn unilateral(budget: Located) -> Decider { - |price| { - struct Choreo{ - price: Located, - budget: Located, - } - impl Choreography> for Choreo { - type L = LocationSet!(Buyer1, Buyer2); - fn run(self, op: &impl ChoreoOp) -> Located { - op.locally(Buyer1, |un| { - un.unwrap(&self.price) <= un.unwrap(&self.budget) - }) - } - } - Box::new(Choreo{price, budget}) - } +trait Decider { + type Budgets; + fn new(price: Located, Buyer1>, budgets: Self::Budgets) -> Self; } -//fn collaborative(budget1: Located, budget2: Located) -> Decider { -// |price| { -// impl Choreography> for TwoBuyerDecider { - // type L = LocationSet!(Buyer1, Buyer2); - // fn run(self, op: &impl ChoreoOp) -> Located { - // let remaining = op.locally(Buyer1, |un| { - // const BUYER1_BUDGET: Money = 100; - // return un.unwrap(&self.price) - BUYER1_BUDGET; - // }); - // let remaining = op.comm(Buyer1, Buyer2, &remaining); - //let decision = op.locally(Buyer2, |un| { - // const BUYER2_BUDGET: Money = 200; - // return *un.unwrap(&remaining) < BUYER2_BUDGET; - // }); - // op.comm(Buyer2, Buyer1, &decision) - //} -//} -// } -//} - - -struct BooksellerChoreography{ - decider: Decider, +struct Booksellers> + Decider, Budgets> { inventory: Located, title: Located, + budgets: Budgets, + _marker: PhantomData, } -impl Choreography> for BooksellerChoreography +impl, L = LocationSet!(Buyer1, Buyer2)> + Decider, Budgets> + Choreography> for Booksellers { type L = LocationSet!(Buyer1, Buyer2, Seller); fn run(self, op: &impl ChoreoOp) -> Option { @@ -79,13 +47,10 @@ impl Choreography> for BooksellerChoreography let price_at_seller = op.locally(Seller, |un| { let inventory = un.unwrap(&self.inventory); let title = un.unwrap(&title_at_seller); - match inventory.get(title) { - Some((price, _)) => Some(price), - None => None, - } - }); + inventory.get(title).map(|(price, _)|{*price}) }); let price_at_buyer1 = op.comm(Seller, Buyer1, &price_at_seller); - let decision_at_buyer1 = op.enclave(&self.decider(price_at_buyer1)); + let decider = D::new(price_at_buyer1, self.budgets); + let decision_at_buyer1 = op.call(decider); struct GetDeliveryDateChoreography { inventory: Located, @@ -100,10 +65,7 @@ impl Choreography> for BooksellerChoreography let delivery_date_at_seller = op.locally(Seller, |un| { let title = un.unwrap(&self.title_at_seller); let inventory = un.unwrap(&self.inventory); - match inventory.get(title) { - Some((_, delivery_date)) => Some(delivery_date), - None => None, - } + inventory.get(title).map(|(_, delivery_date)| {*delivery_date}) }); let delivery_date_at_buyer1 = op.comm(Seller, Buyer1, &delivery_date_at_seller); return delivery_date_at_buyer1; @@ -113,47 +75,65 @@ impl Choreography> for BooksellerChoreography } } - return op.enclave(GetDeliveryDateChoreography { - inventory: self.inventory.clone(), - title_at_seller: title_at_seller.clone(), - decision_at_buyer1, - }); + return op.broadcast(Buyer1, + op.enclave(GetDeliveryDateChoreography { + inventory: self.inventory.clone(), + title_at_seller: title_at_seller.clone(), + decision_at_buyer1, + }).flatten()); } } -/*fn locate(a: A, p: P) -> Located { - struct Dummy { - a: RC, - p: P1 +struct Unilateral { + price: Located, Buyer1>, + budget: Located, +} +impl Decider for Unilateral { + type Budgets = Located; + fn new(price: Located, Buyer1>, budgets: Located) -> Self{ + return Self{price: price, budget: budgets} } - impl Choreography> for Dummy - where P2: ChoreographyLocation { - type L = LocationSet!(P2); - fn run(self, op: &impl ChoreoOp) -> Located { - op.locally(self.p, |_| self.a) - } +} +impl Choreography> for Unilateral { + type L = LocationSet!(Buyer1, Buyer2); + fn run(self, op: &impl ChoreoOp) -> Located { + op.locally(Buyer1, |un| { + match un.unwrap(&self.price) { + Some(price) => price <= un.unwrap(&self.budget), + None => false + } + }) } - Runner::new().run(Dummy{a, p}); -}*/ +} -fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option, answer: Option) { - let central_runner = Runner::new(); - let decider: Decider = match budget2 { - Some(b2) => panic!(), - None => unilateral(central_runner.local(budget1)), - }; - let central_result = central_runner.run(BooksellerChoreography{ - decider, - inventory: central_runner.local(inventory), - title: central_runner.local(title), - }); - assert_eq!(central_result, answer); +//fn collaborative(budget1: Located, budget2: Located) -> Decider { +// |price| { +// impl Choreography> for TwoBuyerDecider { + // type L = LocationSet!(Buyer1, Buyer2); + // fn run(self, op: &impl ChoreoOp) -> Located { + // let remaining = op.locally(Buyer1, |un| { + // const BUYER1_BUDGET: Money = 100; + // return un.unwrap(&self.price) - BUYER1_BUDGET; + // }); + // let remaining = op.comm(Buyer1, Buyer2, &remaining); + //let decision = op.locally(Buyer2, |un| { + // const BUYER2_BUDGET: Money = 200; + // return *un.unwrap(&remaining) < BUYER2_BUDGET; + // }); + // op.comm(Buyer2, Buyer1, &decision) + //} +//} +// } +//} + + + +fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option, answer: Option) { let transport_channel = LocalTransportChannelBuilder::new() .with(Seller).with(Buyer1).with(Buyer2) .build(); - let seller_projector = Arc::new(Projector::new( Seller, LocalTransport::new(Seller, transport_channel.clone()), @@ -168,38 +148,54 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< )); let mut handles = Vec::new(); - if let Some(budget2) = budget2 { - + if let Some(_) = budget2 { + panic!() //TODO } else { + { + let central_runner = Runner::new(); + let choreo : Booksellers> = Booksellers{ + inventory: central_runner.local(inventory.clone()), + title: central_runner.local(title.clone()), + budgets: central_runner.local(budget1), + _marker: PhantomData, + }; + let central_result = central_runner.run(choreo); + assert_eq!(central_result, answer); + } { let seller_projector = seller_projector.clone(); - let inventory = inventory.clone(); handles.push(thread::spawn(move || { - seller_projector.epp_and_run(BooksellerChoreography { - decider, - inventory: seller_projector.local(inventory), + let choreo : Booksellers> = Booksellers{ + inventory: seller_projector.local(inventory.clone()), title: seller_projector.remote(Buyer1), - }); + budgets: seller_projector.remote(Buyer1), + _marker: PhantomData, + }; + seller_projector.epp_and_run(choreo) })); } { let buyer1_projector = buyer1_projector.clone(); handles.push(thread::spawn(move || { - buyer1_projector.epp_and_run(BooksellerChoreography { - decider, + let choreo : Booksellers> = Booksellers{ inventory: buyer1_projector.remote(Seller), - title: buyer1_projector.local("HoTT".to_string()), - }); + title: buyer1_projector.local(title).clone(), + budgets: buyer1_projector.local(budget1), + _marker: PhantomData, + }; + buyer1_projector.epp_and_run(choreo) })); } { let buyer2_projector = buyer2_projector.clone(); handles.push(thread::spawn(move || { - buyer2_projector.epp_and_run(BooksellerChoreography { - decider, + let choreo : Booksellers> = Booksellers{ inventory: buyer2_projector.remote(Seller), title: buyer2_projector.remote(Buyer1), - }); + budgets: buyer2_projector.remote(Buyer1), + _marker: PhantomData, + }; + buyer2_projector.epp_and_run(choreo) })); } } From b00cff9c78f4c5b0bb2d6dc52653339e35e3ee1f Mon Sep 17 00:00:00 2001 From: Mako Bates <33739499+ShapeOfMatter@users.noreply.github.com> Date: Fri, 18 Oct 2024 22:27:25 -0400 Subject: [PATCH 22/42] Nominally ready test case. --- chorus_lib/tests/booksellers.rs | 108 +++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 30 deletions(-) diff --git a/chorus_lib/tests/booksellers.rs b/chorus_lib/tests/booksellers.rs index e148583..f6daa9e 100644 --- a/chorus_lib/tests/booksellers.rs +++ b/chorus_lib/tests/booksellers.rs @@ -1,6 +1,7 @@ extern crate chorus_lib; use std::collections::HashMap; +use std::cmp::max; use std::marker::PhantomData; use std::sync::Arc; use std::thread; @@ -107,30 +108,36 @@ impl Choreography> for Unilateral { } -//fn collaborative(budget1: Located, budget2: Located) -> Decider { -// |price| { -// impl Choreography> for TwoBuyerDecider { - // type L = LocationSet!(Buyer1, Buyer2); - // fn run(self, op: &impl ChoreoOp) -> Located { - // let remaining = op.locally(Buyer1, |un| { - // const BUYER1_BUDGET: Money = 100; - // return un.unwrap(&self.price) - BUYER1_BUDGET; - // }); - // let remaining = op.comm(Buyer1, Buyer2, &remaining); - //let decision = op.locally(Buyer2, |un| { - // const BUYER2_BUDGET: Money = 200; - // return *un.unwrap(&remaining) < BUYER2_BUDGET; - // }); - // op.comm(Buyer2, Buyer1, &decision) - //} -//} -// } -//} - - +//////////////////////////////////////////////////////////////////////// +struct Colaborative { + price: Located, Buyer1>, + budget1: Located, + budget2: Located, +} +impl Decider for Colaborative { + type Budgets = (Located, Located); + fn new(price: Located, Buyer1>, (budget1, budget2): (Located, Located)) -> Self{ + return Self{price: price, budget1: budget1, budget2: budget2} + } +} +impl Choreography> for Colaborative { + type L = LocationSet!(Buyer1, Buyer2); + fn run(self, op: &impl ChoreoOp) -> Located { + match op.broadcast(Buyer1, self.price) { + Some(price) => { + let remainder = op.comm(Buyer2, Buyer1, &op.locally(Buyer2, |un| { + max(0, price - un.unwrap(&self.budget2)) + })); + op.locally(Buyer1, |un| {un.unwrap(&remainder) <= un.unwrap(&self.budget1)}) + }, + None => op.locally(Buyer1, |_| {false}) + } + } +} fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option, answer: Option) { + let central_runner = Runner::new(); let transport_channel = LocalTransportChannelBuilder::new() .with(Seller).with(Buyer1).with(Buyer2) .build(); @@ -148,11 +155,52 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< )); let mut handles = Vec::new(); - if let Some(_) = budget2 { - panic!() //TODO + if let Some(budget2) = budget2 { + { + let choreo : Booksellers, Located)> = Booksellers{ + inventory: central_runner.local(inventory.clone()), + title: central_runner.local(title.clone()), + budgets: (central_runner.local(budget1), central_runner.local(budget2)), + _marker: PhantomData, + }; + let central_result = central_runner.run(choreo); + assert_eq!(central_result, answer); + } + { + handles.push(thread::spawn(move || { + let choreo : Booksellers, Located)> = Booksellers{ + inventory: seller_projector.local(inventory.clone()), + title: seller_projector.remote(Buyer1), + budgets: (seller_projector.remote(Buyer1), seller_projector.remote(Buyer2)), + _marker: PhantomData, + }; + seller_projector.epp_and_run(choreo) + })); + } + { + handles.push(thread::spawn(move || { + let choreo : Booksellers, Located)> = Booksellers{ + inventory: buyer1_projector.remote(Seller), + title: buyer1_projector.local(title).clone(), + budgets: (buyer1_projector.local(budget1), buyer1_projector.remote(Buyer2)), + _marker: PhantomData, + }; + buyer1_projector.epp_and_run(choreo) + })); + } + { + handles.push(thread::spawn(move || { + let choreo : Booksellers, Located)> = Booksellers{ + inventory: buyer2_projector.remote(Seller), + title: buyer2_projector.remote(Buyer1), + budgets: (buyer2_projector.remote(Buyer1), buyer2_projector.local(budget2)), + _marker: PhantomData, + }; + buyer2_projector.epp_and_run(choreo) + })); + } } else { { - let central_runner = Runner::new(); let choreo : Booksellers> = Booksellers{ inventory: central_runner.local(inventory.clone()), title: central_runner.local(title.clone()), @@ -163,7 +211,6 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< assert_eq!(central_result, answer); } { - let seller_projector = seller_projector.clone(); handles.push(thread::spawn(move || { let choreo : Booksellers> = Booksellers{ inventory: seller_projector.local(inventory.clone()), @@ -175,7 +222,6 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< })); } { - let buyer1_projector = buyer1_projector.clone(); handles.push(thread::spawn(move || { let choreo : Booksellers> = Booksellers{ inventory: buyer1_projector.remote(Seller), @@ -187,7 +233,6 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< })); } { - let buyer2_projector = buyer2_projector.clone(); handles.push(thread::spawn(move || { let choreo : Booksellers> = Booksellers{ inventory: buyer2_projector.remote(Seller), @@ -219,7 +264,10 @@ fn main() { ); i }; - let title = "TAPL".to_string(); - let budget = 300; - run_test(inventory, title, budget, None, Some(NaiveDate::from_ymd_opt(2023, 8, 3).unwrap())) + let tapl = "TAPL".to_string(); + let hott = "HoTT".to_string(); + run_test(inventory.clone(), tapl.clone(), 100, None, Some(NaiveDate::from_ymd_opt(2023, 8, 3).unwrap())); + run_test(inventory.clone(), hott.clone(), 25, None, None); + run_test(inventory.clone(), tapl.clone(), 30, Some(30), Some(NaiveDate::from_ymd_opt(2023, 8, 3).unwrap())); + run_test(inventory.clone(), hott.clone(), 30, Some(30), None); } From cef67813e29380b5efa8b08478ebcb63e35a2952 Mon Sep 17 00:00:00 2001 From: Mako Bates <33739499+ShapeOfMatter@users.noreply.github.com> Date: Sun, 20 Oct 2024 10:50:59 -0400 Subject: [PATCH 23/42] differential enclaving in booksellers deciders. --- chorus_lib/tests/booksellers.rs | 90 ++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 25 deletions(-) diff --git a/chorus_lib/tests/booksellers.rs b/chorus_lib/tests/booksellers.rs index f6daa9e..9d5ef9c 100644 --- a/chorus_lib/tests/booksellers.rs +++ b/chorus_lib/tests/booksellers.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use std::thread; use chorus_lib::{ - core::{ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, Projector, Runner}, + core::{ChoreoOp, Choreography, ChoreographyLocation, HCons, Here, Located, LocationSet, Member, Projector, Runner, Subset, There}, transport::local::{LocalTransport, LocalTransportChannelBuilder}, }; use chrono::NaiveDate; @@ -32,17 +32,24 @@ trait Decider { fn new(price: Located, Buyer1>, budgets: Self::Budgets) -> Self; } -struct Booksellers> + Decider, Budgets> { +struct Booksellers> + Decider, Budgets, Buyers, B1Index, BSIndex> { inventory: Located, title: Located, budgets: Budgets, - _marker: PhantomData, + _phantoms: PhantomData<(D, Buyers, B1Index, BSIndex)>, } -impl, L = LocationSet!(Buyer1, Buyer2)> + Decider, Budgets> - Choreography> for Booksellers +impl, L = Buyers> + Decider + ,Budgets + ,B1Index + ,BSIndex + > Choreography> for Booksellers + where Buyer1: Member + , Buyer1: Member, There> + , Buyers: Subset, BSIndex> { - type L = LocationSet!(Buyer1, Buyer2, Seller); + type L = HCons; fn run(self, op: &impl ChoreoOp) -> Option { let title_at_seller = op.comm(Buyer1, Seller, &self.title); let price_at_seller = op.locally(Seller, |un| { @@ -51,7 +58,7 @@ impl, L = LocationSet!(Buyer1, Buyer2)> + inventory.get(title).map(|(price, _)|{*price}) }); let price_at_buyer1 = op.comm(Seller, Buyer1, &price_at_seller); let decider = D::new(price_at_buyer1, self.budgets); - let decision_at_buyer1 = op.call(decider); + let decision_at_buyer1 = op.enclave(decider).flatten(); struct GetDeliveryDateChoreography { inventory: Located, @@ -96,7 +103,7 @@ impl Decider for Unilateral { } } impl Choreography> for Unilateral { - type L = LocationSet!(Buyer1, Buyer2); + type L = LocationSet!(Buyer1); fn run(self, op: &impl ChoreoOp) -> Located { op.locally(Buyer1, |un| { match un.unwrap(&self.price) { @@ -137,7 +144,6 @@ impl Choreography> for Colaborative { fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option, answer: Option) { - let central_runner = Runner::new(); let transport_channel = LocalTransportChannelBuilder::new() .with(Seller).with(Buyer1).with(Buyer2) .build(); @@ -157,88 +163,122 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< if let Some(budget2) = budget2 { { - let choreo : Booksellers, Located)> = Booksellers{ + let central_runner = Runner::new(); + let choreo : Booksellers, Located), + LocationSet!(Buyer1, Buyer2), + Here, + (There, (There>, Here))> = Booksellers{ inventory: central_runner.local(inventory.clone()), title: central_runner.local(title.clone()), budgets: (central_runner.local(budget1), central_runner.local(budget2)), - _marker: PhantomData, + _phantoms: PhantomData, }; let central_result = central_runner.run(choreo); assert_eq!(central_result, answer); } { handles.push(thread::spawn(move || { - let choreo : Booksellers, Located)> = Booksellers{ + let choreo : Booksellers, Located), + LocationSet!(Buyer1, Buyer2), + Here, + (There, (There>, Here))> = Booksellers{ inventory: seller_projector.local(inventory.clone()), title: seller_projector.remote(Buyer1), budgets: (seller_projector.remote(Buyer1), seller_projector.remote(Buyer2)), - _marker: PhantomData, + _phantoms: PhantomData, }; seller_projector.epp_and_run(choreo) })); } { handles.push(thread::spawn(move || { - let choreo : Booksellers, Located)> = Booksellers{ + let choreo : Booksellers, Located), + LocationSet!(Buyer1, Buyer2), + Here, + (There, (There>, Here))> = Booksellers{ inventory: buyer1_projector.remote(Seller), title: buyer1_projector.local(title).clone(), budgets: (buyer1_projector.local(budget1), buyer1_projector.remote(Buyer2)), - _marker: PhantomData, + _phantoms: PhantomData, }; buyer1_projector.epp_and_run(choreo) })); } { handles.push(thread::spawn(move || { - let choreo : Booksellers, Located)> = Booksellers{ + let choreo : Booksellers, Located), + LocationSet!(Buyer1, Buyer2), + Here, + (There, (There>, Here))> = Booksellers{ inventory: buyer2_projector.remote(Seller), title: buyer2_projector.remote(Buyer1), budgets: (buyer2_projector.remote(Buyer1), buyer2_projector.local(budget2)), - _marker: PhantomData, + _phantoms: PhantomData, }; buyer2_projector.epp_and_run(choreo) })); } } else { { - let choreo : Booksellers> = Booksellers{ + let central_runner = Runner::new(); + let choreo : Booksellers, + LocationSet!(Buyer1), + Here, + (There, Here)> = Booksellers{ inventory: central_runner.local(inventory.clone()), title: central_runner.local(title.clone()), budgets: central_runner.local(budget1), - _marker: PhantomData, + _phantoms: PhantomData, }; let central_result = central_runner.run(choreo); assert_eq!(central_result, answer); } { handles.push(thread::spawn(move || { - let choreo : Booksellers> = Booksellers{ + let choreo : Booksellers, + LocationSet!(Buyer1), + Here, + (There, Here)> = Booksellers{ inventory: seller_projector.local(inventory.clone()), title: seller_projector.remote(Buyer1), budgets: seller_projector.remote(Buyer1), - _marker: PhantomData, + _phantoms: PhantomData, }; seller_projector.epp_and_run(choreo) })); } { handles.push(thread::spawn(move || { - let choreo : Booksellers> = Booksellers{ + let choreo : Booksellers, + LocationSet!(Buyer1), + Here, + (There, Here)> = Booksellers{ inventory: buyer1_projector.remote(Seller), title: buyer1_projector.local(title).clone(), budgets: buyer1_projector.local(budget1), - _marker: PhantomData, + _phantoms: PhantomData, }; buyer1_projector.epp_and_run(choreo) })); } { handles.push(thread::spawn(move || { - let choreo : Booksellers> = Booksellers{ + let choreo : Booksellers, + LocationSet!(Buyer1), + Here, + (There, Here)> = Booksellers{ inventory: buyer2_projector.remote(Seller), title: buyer2_projector.remote(Buyer1), budgets: buyer2_projector.remote(Buyer1), - _marker: PhantomData, + _phantoms: PhantomData, }; buyer2_projector.epp_and_run(choreo) })); From 389bd29e64061fd6e71c419c1b125284a4210e6b Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Thu, 24 Oct 2024 19:31:13 -0700 Subject: [PATCH 24/42] clean up old multicast api --- chorus_lib/examples/multicast.rs | 4 +--- chorus_lib/src/core.rs | 41 +++----------------------------- 2 files changed, 4 insertions(+), 41 deletions(-) diff --git a/chorus_lib/examples/multicast.rs b/chorus_lib/examples/multicast.rs index 0a514a7..4c24913 100644 --- a/chorus_lib/examples/multicast.rs +++ b/chorus_lib/examples/multicast.rs @@ -2,9 +2,7 @@ extern crate chorus_lib; use std::thread; -use chorus_lib::core::{ - ChoreoOp, Choreography, ChoreographyLocation, LocationSet, MulticastBuilder, Projector, -}; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, LocationSet, Projector}; use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; // --- Define two locations (Alice and Bob) --- diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 84f6449..3f01c85 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -350,43 +350,6 @@ impl Unwrapper { } } -/// TODO: documentation -pub struct MulticastBuilder { - data: Located, - phantom: PhantomData<(L, D)>, -} - -impl MulticastBuilder { - /// Constructs a `MulticastBuilder` struct. - /// - /// - `source` is the source location of the multicast. - /// - `data` is the located value to be multicast. - pub fn new(source: S, data: Located) -> MulticastBuilder { - MulticastBuilder { - data, - phantom: PhantomData, - } - } -} - -impl - MulticastBuilder -{ - /// Adds a destination to the multicast. - pub fn to( - self, - _destination: D1, - ) -> MulticastBuilder> - where - D1: Member, - { - MulticastBuilder { - data: self.data, - phantom: PhantomData, - } - } -} - /// Provides choreographic operations. /// /// The trait provides methods to work with located values. An implementation of the trait is "injected" into @@ -443,7 +406,9 @@ pub trait ChoreoOp { L: Subset, Sender: Member; - /// TODO: documentation + /// Performs a multicast from a location to a set of locations. + /// + /// Use `::new()` to create a value of location set. fn multicast( &self, src: Sender, From 8e707fbb68196612ca35b0c17709466d8726720c Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Thu, 24 Oct 2024 19:43:16 -0700 Subject: [PATCH 25/42] add `Quire` public API --- chorus_lib/src/core.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 3f01c85..5236865 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -136,6 +136,35 @@ where } } +impl Quire { + /// Constructs a struct located at the current location with value + pub fn new() -> Self { + Quire { + value: HashMap::new(), + phantom: PhantomData, + } + } +} + +impl Quire +where + L: LocationSet, +{ + /// Add a value located at a location + pub fn add(self, _location: L1, value: V) -> Quire> { + let mut map = self.value; + map.insert(L1::name().to_string(), value); + Quire { + value: map, + phantom: PhantomData, + } + } + /// Get as a hash map + pub fn get_map(self) -> HashMap { + self.value + } +} + /// Represents possibly different values located at multiple locations #[derive(Debug)] pub struct Faceted From f10fd90ff0ca5bb13c13d6a957c787a243f79c3f Mon Sep 17 00:00:00 2001 From: Mako Bates <33739499+ShapeOfMatter@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:02:33 -0400 Subject: [PATCH 26/42] WIP --- chorus_lib/tests/booksellers.rs | 68 +++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/chorus_lib/tests/booksellers.rs b/chorus_lib/tests/booksellers.rs index 9d5ef9c..ff1a1d7 100644 --- a/chorus_lib/tests/booksellers.rs +++ b/chorus_lib/tests/booksellers.rs @@ -1,13 +1,15 @@ extern crate chorus_lib; use std::collections::HashMap; -use std::cmp::max; use std::marker::PhantomData; use std::sync::Arc; use std::thread; use chorus_lib::{ - core::{ChoreoOp, Choreography, ChoreographyLocation, HCons, Here, Located, LocationSet, Member, Projector, Runner, Subset, There}, + core::{ChoreoOp, Choreography, ChoreographyLocation, Faceted, + FanInChoreography, + HCons, Here, Located, LocationSet, Member, MultiplyLocated, + Projector, Runner, Subset, There}, transport::local::{LocalTransport, LocalTransportChannelBuilder}, }; use chrono::NaiveDate; @@ -116,26 +118,62 @@ impl Choreography> for Unilateral { //////////////////////////////////////////////////////////////////////// -struct Colaborative { +struct Colaborative { price: Located, Buyer1>, - budget1: Located, - budget2: Located, + budgets: Faceted, + _phantoms: PhantomData, } -impl Decider for Colaborative { - type Budgets = (Located, Located); - fn new(price: Located, Buyer1>, (budget1, budget2): (Located, Located)) -> Self{ - return Self{price: price, budget1: budget1, budget2: budget2} +impl Decider for Colaborative +where Buyer1: Member +{ + type Budgets = Faceted; + fn new(price: Located, Buyer1>, budgets: Faceted) -> Self{ + return Self{price: price, budgets: budgets} } } -impl Choreography> for Colaborative { - type L = LocationSet!(Buyer1, Buyer2); +impl Choreography> for Colaborative +where Buyer1: Member +{ + type L = Buyers; fn run(self, op: &impl ChoreoOp) -> Located { match op.broadcast(Buyer1, self.price) { Some(price) => { - let remainder = op.comm(Buyer2, Buyer1, &op.locally(Buyer2, |un| { - max(0, price - un.unwrap(&self.budget2)) - })); - op.locally(Buyer1, |un| {un.unwrap(&remainder) <= un.unwrap(&self.budget1)}) + + struct Gather<'a, Buyers: LocationSet, B1Index> { + budgets: &'a Faceted, + _phantoms: PhantomData, + } + impl<'a, Buyers: LocationSet, B1Index> FanInChoreography for Gather<'a, Buyers, B1Index> + where Buyer1: Member + { + type L = Buyers; + type QS = Buyers; + type RS = LocationSet!(Buyer1); + fn run( + &self, + op: &impl ChoreoOp, + ) -> Located + where + Self::QS: Subset, + Self::RS: Subset, + Q: Member, + Q: Member, + { + op.comm(Q::new(), + Buyer1, + op.locally(Q::new(), |un| *un.unwrap3(&self.budgets))) + } + } + let budgets = op.fanin( + Buyers::new(), + Gather { + budgets: &self.budget, + _phantoms: PhantomData, + }, + ); + + let total = op.locally(Buyer1, |un| { un.unwrap(budgets).into_iter().sum() }); + return price <= total }, None => op.locally(Buyer1, |_| {false}) } From 08b54a4bce5f11470762a1c98384fa6d08d993f7 Mon Sep 17 00:00:00 2001 From: Mako Bates <33739499+ShapeOfMatter@users.noreply.github.com> Date: Fri, 25 Oct 2024 13:09:18 -0400 Subject: [PATCH 27/42] probably just missing reflexivity --- chorus_lib/tests/booksellers.rs | 75 ++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 21 deletions(-) diff --git a/chorus_lib/tests/booksellers.rs b/chorus_lib/tests/booksellers.rs index ff1a1d7..c1c3791 100644 --- a/chorus_lib/tests/booksellers.rs +++ b/chorus_lib/tests/booksellers.rs @@ -8,8 +8,10 @@ use std::thread; use chorus_lib::{ core::{ChoreoOp, Choreography, ChoreographyLocation, Faceted, FanInChoreography, - HCons, Here, Located, LocationSet, Member, MultiplyLocated, - Projector, Runner, Subset, There}, + HCons, HNil, + Here, Located, LocationSet, Member, + Projector, + Quire, Runner, Subset, There}, transport::local::{LocalTransport, LocalTransportChannelBuilder}, }; use chrono::NaiveDate; @@ -128,7 +130,7 @@ where Buyer1: Member { type Budgets = Faceted; fn new(price: Located, Buyer1>, budgets: Faceted) -> Self{ - return Self{price: price, budgets: budgets} + return Self{price: price, budgets: budgets, _phantoms: PhantomData} } } impl Choreography> for Colaborative @@ -159,21 +161,26 @@ where Buyer1: Member Q: Member, Q: Member, { - op.comm(Q::new(), - Buyer1, - op.locally(Q::new(), |un| *un.unwrap3(&self.budgets))) + op.comm::, Q, Buyer1, Money, (QMemberL, Here), QMemberL, B1Index>( + Q::new(), + Buyer1, + &op.locally::(Q::new(), + |un| *un.unwrap3::(&self.budgets)) + ) } } let budgets = op.fanin( Buyers::new(), Gather { - budgets: &self.budget, + budgets: &self.budgets, _phantoms: PhantomData, }, ); - let total = op.locally(Buyer1, |un| { un.unwrap(budgets).into_iter().sum() }); - return price <= total + op.locally(Buyer1, |un| { + let budget = un.unwrap::, HCons, Here>(&budgets).get_map().into_values().sum(); + return price <= budget + }) }, None => op.locally(Buyer1, |_| {false}) } @@ -202,14 +209,20 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< if let Some(budget2) = budget2 { { let central_runner = Runner::new(); - let choreo : Booksellers, Located), + let choreo : Booksellers, + Faceted, LocationSet!(Buyer1, Buyer2), Here, (There, (There>, Here))> = Booksellers{ inventory: central_runner.local(inventory.clone()), title: central_runner.local(title.clone()), - budgets: (central_runner.local(budget1), central_runner.local(budget2)), + budgets: Faceted { + value: HashMap::from([ + (String::from(Buyer1::name()), budget1), + (String::from(Buyer2::name()), budget2), + ]), + phantom: PhantomData, + }, _phantoms: PhantomData, }; let central_result = central_runner.run(choreo); @@ -217,14 +230,20 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } { handles.push(thread::spawn(move || { - let choreo : Booksellers, Located), + let choreo : Booksellers, + Faceted, LocationSet!(Buyer1, Buyer2), Here, (There, (There>, Here))> = Booksellers{ inventory: seller_projector.local(inventory.clone()), title: seller_projector.remote(Buyer1), - budgets: (seller_projector.remote(Buyer1), seller_projector.remote(Buyer2)), + budgets: Faceted { + value: HashMap::from([ + (String::from(Buyer1::name()), budget1), + (String::from(Buyer2::name()), budget2), + ]), + phantom: PhantomData, + }, _phantoms: PhantomData, }; seller_projector.epp_and_run(choreo) @@ -232,14 +251,20 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } { handles.push(thread::spawn(move || { - let choreo : Booksellers, Located), + let choreo : Booksellers, + Faceted, LocationSet!(Buyer1, Buyer2), Here, (There, (There>, Here))> = Booksellers{ inventory: buyer1_projector.remote(Seller), title: buyer1_projector.local(title).clone(), - budgets: (buyer1_projector.local(budget1), buyer1_projector.remote(Buyer2)), + budgets: Faceted { + value: HashMap::from([ + (String::from(Buyer1::name()), budget1), + (String::from(Buyer2::name()), budget2), + ]), + phantom: PhantomData, + }, _phantoms: PhantomData, }; buyer1_projector.epp_and_run(choreo) @@ -247,14 +272,20 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } { handles.push(thread::spawn(move || { - let choreo : Booksellers, Located), + let choreo : Booksellers, + Faceted, LocationSet!(Buyer1, Buyer2), Here, (There, (There>, Here))> = Booksellers{ inventory: buyer2_projector.remote(Seller), title: buyer2_projector.remote(Buyer1), - budgets: (buyer2_projector.remote(Buyer1), buyer2_projector.local(budget2)), + budgets: Faceted { + value: HashMap::from([ + (String::from(Buyer1::name()), budget1), + (String::from(Buyer2::name()), budget2), + ]), + phantom: PhantomData, + }, _phantoms: PhantomData, }; buyer2_projector.epp_and_run(choreo) @@ -348,4 +379,6 @@ fn main() { run_test(inventory.clone(), hott.clone(), 25, None, None); run_test(inventory.clone(), tapl.clone(), 30, Some(30), Some(NaiveDate::from_ymd_opt(2023, 8, 3).unwrap())); run_test(inventory.clone(), hott.clone(), 30, Some(30), None); + run_test(inventory.clone(), "nonesuch".to_string(), 25, None, None); + run_test(inventory.clone(), "nonesuch".to_string(), 30, Some(30), None); } From 7c03eb19cd6eb830b9561d2ca4fb7b37203cf0ef Mon Sep 17 00:00:00 2001 From: Mako Bates <33739499+ShapeOfMatter@users.noreply.github.com> Date: Tue, 29 Oct 2024 21:47:30 -0400 Subject: [PATCH 28/42] make quire.get_map be more conventional and work. WIP in booksellers. --- chorus_lib/src/core.rs | 15 +++++++++++++-- chorus_lib/tests/booksellers.rs | 28 +++++++++++++++++----------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 82ce08b..b4d6f31 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -159,12 +159,23 @@ where phantom: PhantomData, } } - /// Get as a hash map - pub fn get_map(self) -> HashMap { + /// Turn into a hash map + pub fn into_map(self) -> HashMap { self.value } } +impl Quire +where + L: LocationSet, + V: Clone, +{ + /// Get a copy as a hash map + pub fn get_map(&self) -> HashMap { + self.value.clone() + } +} + /// Represents possibly different values located at multiple locations #[derive(Debug)] pub struct Faceted diff --git a/chorus_lib/tests/booksellers.rs b/chorus_lib/tests/booksellers.rs index c1c3791..a6c86b8 100644 --- a/chorus_lib/tests/booksellers.rs +++ b/chorus_lib/tests/booksellers.rs @@ -9,7 +9,9 @@ use chorus_lib::{ core::{ChoreoOp, Choreography, ChoreographyLocation, Faceted, FanInChoreography, HCons, HNil, - Here, Located, LocationSet, Member, + Here, Located, LocationSet, + LocationSetFoldable, + Member, Projector, Quire, Runner, Subset, There}, transport::local::{LocalTransport, LocalTransportChannelBuilder}, @@ -120,21 +122,25 @@ impl Choreography> for Unilateral { //////////////////////////////////////////////////////////////////////// -struct Colaborative { +struct Colaborative { price: Located, Buyer1>, budgets: Faceted, - _phantoms: PhantomData, + _phantoms: PhantomData<(B1Index, BSRefl, BSFld)>, } -impl Decider for Colaborative -where Buyer1: Member +impl Decider for Colaborative +where Buyer1: Member, + Buyers: Subset { type Budgets = Faceted; fn new(price: Located, Buyer1>, budgets: Faceted) -> Self{ return Self{price: price, budgets: budgets, _phantoms: PhantomData} } } -impl Choreography> for Colaborative -where Buyer1: Member +impl + Choreography> for Colaborative +where Buyer1: Member, + Buyers: Subset, + Buyers: LocationSetFoldable { type L = Buyers; fn run(self, op: &impl ChoreoOp) -> Located { @@ -209,7 +215,7 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< if let Some(budget2) = budget2 { { let central_runner = Runner::new(); - let choreo : Booksellers, + let choreo : Booksellers, Faceted, LocationSet!(Buyer1, Buyer2), Here, @@ -230,7 +236,7 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } { handles.push(thread::spawn(move || { - let choreo : Booksellers, + let choreo : Booksellers, Faceted, LocationSet!(Buyer1, Buyer2), Here, @@ -251,7 +257,7 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } { handles.push(thread::spawn(move || { - let choreo : Booksellers, + let choreo : Booksellers, Faceted, LocationSet!(Buyer1, Buyer2), Here, @@ -272,7 +278,7 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } { handles.push(thread::spawn(move || { - let choreo : Booksellers, + let choreo : Booksellers, Faceted, LocationSet!(Buyer1, Buyer2), Here, From eeb3a9c1684fba87bae047a970328b2d2e2292ca Mon Sep 17 00:00:00 2001 From: Mako Bates <33739499+ShapeOfMatter@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:34:34 -0400 Subject: [PATCH 29/42] get booksellers typechecking. Doesn't actually run yet though --- chorus_lib/src/core.rs | 35 +++++++++++++++- chorus_lib/tests/booksellers.rs | 74 ++++++++++----------------------- 2 files changed, 56 insertions(+), 53 deletions(-) diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index b4d6f31..74a01c7 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -657,14 +657,35 @@ where /// Constructs a `Located` struct *NOT* located at the projection target. /// /// Use this method to run a choreography that takes a located value as an input. - pub fn remote(&self, at: L) -> Located + pub fn remote(&self, _: L) -> Located where L: Member<>::Remainder, Index2>, { - _ = at; Located::remote() } + /// Construct a `Faceted` struct owned by the projection target. + /// + /// Use this method to run a choreography that takes a Faceted value as an input. + pub fn local_faceted(&self, value: V, _: Owners) -> Faceted + where Target: Member, + { + Faceted {value: HashMap::from([(String::from(Target::name()), value)]), + phantom: PhantomData, + } + } + + /// Construct a `Faceted` struct *NOT* owned by the projection target. + /// + /// Use this method to run a choreography that takes a Faceted value as an input. + pub fn remote_faceted(&self, _: Owners) -> Faceted + where Owners: Subset<>::Remainder, Index2>, + { + Faceted {value: HashMap::new(), + phantom: PhantomData, + } + } + /// Unwraps a located value at the projection target. /// /// Use this method to access the located value returned by a choreography. @@ -1133,6 +1154,16 @@ impl Runner { Located::local(value) } + /// Construct a `Faceted` struct from a lookup table. + /// Will almost certainly cause errors if you don't correctly populate the mapping. + /// + /// Use this method to run a choreography that takes a Faceted value as an input. + pub fn unsafe_faceted(&self, values: [(String, V); N], _: Owners) -> Faceted { + Faceted {value: HashMap::from(values), + phantom: PhantomData, + } + } + /// Unwraps a located value /// /// Runner can unwrap a located value at any location diff --git a/chorus_lib/tests/booksellers.rs b/chorus_lib/tests/booksellers.rs index a6c86b8..7754b81 100644 --- a/chorus_lib/tests/booksellers.rs +++ b/chorus_lib/tests/booksellers.rs @@ -8,7 +8,7 @@ use std::thread; use chorus_lib::{ core::{ChoreoOp, Choreography, ChoreographyLocation, Faceted, FanInChoreography, - HCons, HNil, + HCons, Here, Located, LocationSet, LocationSetFoldable, Member, @@ -167,7 +167,7 @@ where Buyer1: Member, Q: Member, Q: Member, { - op.comm::, Q, Buyer1, Money, (QMemberL, Here), QMemberL, B1Index>( + op.comm::<_, Q, Buyer1, Money, (QMemberL, _), QMemberL, B1Index>( Q::new(), Buyer1, &op.locally::(Q::new(), @@ -184,7 +184,7 @@ where Buyer1: Member, ); op.locally(Buyer1, |un| { - let budget = un.unwrap::, HCons, Here>(&budgets).get_map().into_values().sum(); + let budget = un.unwrap::, _, Here>(&budgets).get_map().into_values().sum(); return price <= budget }) }, @@ -215,20 +215,17 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< if let Some(budget2) = budget2 { { let central_runner = Runner::new(); - let choreo : Booksellers, + let choreo : Booksellers, Faceted, LocationSet!(Buyer1, Buyer2), - Here, - (There, (There>, Here))> = Booksellers{ + _, _> = Booksellers{ inventory: central_runner.local(inventory.clone()), title: central_runner.local(title.clone()), - budgets: Faceted { - value: HashMap::from([ - (String::from(Buyer1::name()), budget1), - (String::from(Buyer2::name()), budget2), - ]), - phantom: PhantomData, - }, + budgets: central_runner.unsafe_faceted( + [(String::from(Buyer1::name()), budget1), + (String::from(Buyer2::name()), budget2), + ], + ::new()), _phantoms: PhantomData, }; let central_result = central_runner.run(choreo); @@ -236,20 +233,13 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } { handles.push(thread::spawn(move || { - let choreo : Booksellers, + let choreo : Booksellers, Faceted, LocationSet!(Buyer1, Buyer2), - Here, - (There, (There>, Here))> = Booksellers{ + _, _> = Booksellers{ inventory: seller_projector.local(inventory.clone()), title: seller_projector.remote(Buyer1), - budgets: Faceted { - value: HashMap::from([ - (String::from(Buyer1::name()), budget1), - (String::from(Buyer2::name()), budget2), - ]), - phantom: PhantomData, - }, + budgets: seller_projector.remote_faceted(::new()), _phantoms: PhantomData, }; seller_projector.epp_and_run(choreo) @@ -257,20 +247,13 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } { handles.push(thread::spawn(move || { - let choreo : Booksellers, + let choreo : Booksellers, Faceted, LocationSet!(Buyer1, Buyer2), - Here, - (There, (There>, Here))> = Booksellers{ + _, _> = Booksellers{ inventory: buyer1_projector.remote(Seller), title: buyer1_projector.local(title).clone(), - budgets: Faceted { - value: HashMap::from([ - (String::from(Buyer1::name()), budget1), - (String::from(Buyer2::name()), budget2), - ]), - phantom: PhantomData, - }, + budgets: buyer1_projector.local_faceted(budget1, ::new()), _phantoms: PhantomData, }; buyer1_projector.epp_and_run(choreo) @@ -278,20 +261,13 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } { handles.push(thread::spawn(move || { - let choreo : Booksellers, + let choreo : Booksellers, Faceted, LocationSet!(Buyer1, Buyer2), - Here, - (There, (There>, Here))> = Booksellers{ + _, _> = Booksellers{ inventory: buyer2_projector.remote(Seller), title: buyer2_projector.remote(Buyer1), - budgets: Faceted { - value: HashMap::from([ - (String::from(Buyer1::name()), budget1), - (String::from(Buyer2::name()), budget2), - ]), - phantom: PhantomData, - }, + budgets: buyer2_projector.local_faceted(budget2, ::new()), _phantoms: PhantomData, }; buyer2_projector.epp_and_run(choreo) @@ -303,8 +279,7 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< let choreo : Booksellers, LocationSet!(Buyer1), - Here, - (There, Here)> = Booksellers{ + _, _> = Booksellers{ inventory: central_runner.local(inventory.clone()), title: central_runner.local(title.clone()), budgets: central_runner.local(budget1), @@ -318,8 +293,7 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< let choreo : Booksellers, LocationSet!(Buyer1), - Here, - (There, Here)> = Booksellers{ + _, _> = Booksellers{ inventory: seller_projector.local(inventory.clone()), title: seller_projector.remote(Buyer1), budgets: seller_projector.remote(Buyer1), @@ -333,8 +307,7 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< let choreo : Booksellers, LocationSet!(Buyer1), - Here, - (There, Here)> = Booksellers{ + _, _> = Booksellers{ inventory: buyer1_projector.remote(Seller), title: buyer1_projector.local(title).clone(), budgets: buyer1_projector.local(budget1), @@ -348,8 +321,7 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< let choreo : Booksellers, LocationSet!(Buyer1), - Here, - (There, Here)> = Booksellers{ + _, _> = Booksellers{ inventory: buyer2_projector.remote(Seller), title: buyer2_projector.remote(Buyer1), budgets: buyer2_projector.remote(Buyer1), From bf3b5ba8b3e204bc54ac90b7426873cc6e4c831a Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Wed, 30 Oct 2024 23:02:21 -0700 Subject: [PATCH 30/42] implement fanin and fanout on the runner, add test cases --- chorus_lib/examples/fanin.rs | 197 ++++++++++++++++++++++++++++++++++ chorus_lib/examples/fanout.rs | 88 +++++++++++++-- chorus_lib/src/core.rs | 165 +++++++++++++++++++++++++--- 3 files changed, 423 insertions(+), 27 deletions(-) create mode 100644 chorus_lib/examples/fanin.rs diff --git a/chorus_lib/examples/fanin.rs b/chorus_lib/examples/fanin.rs new file mode 100644 index 0000000..a4678e0 --- /dev/null +++ b/chorus_lib/examples/fanin.rs @@ -0,0 +1,197 @@ +extern crate chorus_lib; + +use std::marker::PhantomData; +use std::thread; + +use chorus_lib::core::{ + ChoreoOp, Choreography, ChoreographyLocation, FanInChoreography, Located, LocationSet, Member, + MultiplyLocated, Projector, Quire, Subset, +}; +use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; + +#[derive(ChoreographyLocation, Debug)] +struct Alice; + +#[derive(ChoreographyLocation, Debug)] +struct Bob; + +#[derive(ChoreographyLocation, Debug)] +struct Carol; + +struct FanIn +where + Alice: Member, +{ + phantom: PhantomData<(L, QS, Alice, AliceMemberL)>, +} + +impl + FanIn +where + Alice: Member, +{ + fn new(_: Alice) -> Self + where + Alice: Member, + { + FanIn { + phantom: PhantomData, + } + } +} + +impl + FanInChoreography for FanIn +where + Alice: Member, +{ + type L = L; + type QS = QS; + type RS = LocationSet!(Alice); + + fn run( + &self, + op: &impl ChoreoOp, + ) -> MultiplyLocated + where + Self::QS: Subset, + Self::RS: Subset, + Q: Member, + Q: Member, + { + let msg_at_q = op.locally(Q::new(), |_| { + format!("{} says hi to {}", Q::name(), Alice::name()) + }); + let msg_at_alice = op.comm(Q::new(), Alice::new(), &msg_at_q); + return msg_at_alice; + } +} + +struct MainChoreography; +impl Choreography, Alice>> for MainChoreography { + type L = LocationSet!(Alice, Bob, Carol); + + fn run( + self, + op: &impl ChoreoOp, + ) -> Located, Alice> { + let v = op.fanin(::new(), FanIn::new(Alice)); + op.locally(Alice, |un| { + let m = un.unwrap(&v).get_map(); + println!( + "Alice received: \"{}\" from Bob and \"{}\" from Carol", + m.get(Bob::name()).unwrap_or(&String::from("ERROR")), + m.get(Carol::name()).unwrap_or(&String::from("ERROR")) + ) + }); + return v; + } +} + +fn main() { + let transport_channel = LocalTransportChannelBuilder::new() + .with(Alice) + .with(Bob) + .with(Carol) + .build(); + let transport_alice = LocalTransport::new(Alice, transport_channel.clone()); + let transport_bob = LocalTransport::new(Bob, transport_channel.clone()); + let transport_carol = LocalTransport::new(Carol, transport_channel.clone()); + + let alice_projector = Projector::new(Alice, transport_alice); + let bob_projector = Projector::new(Bob, transport_bob); + let carol_projector = Projector::new(Carol, transport_carol); + + let mut handles: Vec> = Vec::new(); + handles.push( + thread::Builder::new() + .name("Alice".to_string()) + .spawn(move || { + alice_projector.epp_and_run(MainChoreography); + }) + .unwrap(), + ); + handles.push( + thread::Builder::new() + .name("Bob".to_string()) + .spawn(move || { + bob_projector.epp_and_run(MainChoreography); + }) + .unwrap(), + ); + handles.push( + thread::Builder::new() + .name("Carol".to_string()) + .spawn(move || { + carol_projector.epp_and_run(MainChoreography); + }) + .unwrap(), + ); + for handle in handles { + handle.join().unwrap(); + } +} + +#[cfg(test)] +mod tests { + use chorus_lib::core::Runner; + + use super::*; + + #[test] + fn test_projector() { + let transport_channel = LocalTransportChannelBuilder::new() + .with(Alice) + .with(Bob) + .with(Carol) + .build(); + let transport_alice = LocalTransport::new(Alice, transport_channel.clone()); + let transport_bob = LocalTransport::new(Bob, transport_channel.clone()); + let transport_carol = LocalTransport::new(Carol, transport_channel.clone()); + + let alice_projector = Projector::new(Alice, transport_alice); + let bob_projector = Projector::new(Bob, transport_bob); + let carol_projector = Projector::new(Carol, transport_carol); + + let mut handles: Vec> = Vec::new(); + handles.push( + thread::Builder::new() + .name("Alice".to_string()) + .spawn(move || { + let quire_at_alice = alice_projector.epp_and_run(MainChoreography); + let m = alice_projector.unwrap(quire_at_alice).get_map(); + assert_eq!(m.get(Bob::name()).unwrap(), "Bob says hi to Alice"); + assert_eq!(m.get(Carol::name()).unwrap(), "Carol says hi to Alice"); + }) + .unwrap(), + ); + handles.push( + thread::Builder::new() + .name("Bob".to_string()) + .spawn(move || { + bob_projector.epp_and_run(MainChoreography); + }) + .unwrap(), + ); + handles.push( + thread::Builder::new() + .name("Carol".to_string()) + .spawn(move || { + carol_projector.epp_and_run(MainChoreography); + }) + .unwrap(), + ); + for handle in handles { + handle.join().unwrap(); + } + } + + #[test] + fn test_runner() { + let runner = Runner::new(); + let quire_at_alice = runner.run(MainChoreography); + let m = runner.unwrap(quire_at_alice).get_map(); + assert_eq!(m.get(Bob::name()).unwrap(), "Bob says hi to Alice"); + assert_eq!(m.get(Carol::name()).unwrap(), "Carol says hi to Alice"); + } +} diff --git a/chorus_lib/examples/fanout.rs b/chorus_lib/examples/fanout.rs index 9ac0caa..a73d25a 100644 --- a/chorus_lib/examples/fanout.rs +++ b/chorus_lib/examples/fanout.rs @@ -67,17 +67,22 @@ where } } -struct ParallelChoreography; -impl Choreography for ParallelChoreography { +struct MainChoreography; +impl Choreography<(Located, Located)> for MainChoreography { type L = LocationSet!(Alice, Bob, Carol); - fn run(self, op: &impl ChoreoOp) { - let v = op.fanout(::new(), FanOut::new(Bob)); - op.locally(Bob, |un| { - println!("{}", un.unwrap3(&v)); + fn run(self, op: &impl ChoreoOp) -> (Located, Located) { + let v = op.fanout(::new(), FanOut::new(Alice)); + let value_at_bob = op.locally(Bob, |un| { + let v = un.unwrap3(&v); + println!("{}", v); + v.clone() }); - op.locally(Carol, |un| { - println!("{}", un.unwrap3(&v)); + let value_at_carol = op.locally(Carol, |un| { + let v = un.unwrap3(&v); + println!("{}", v); + v.clone() }); + return (value_at_bob, value_at_carol); } } @@ -100,7 +105,7 @@ fn main() { thread::Builder::new() .name("Alice".to_string()) .spawn(move || { - alice_projector.epp_and_run(ParallelChoreography); + alice_projector.epp_and_run(MainChoreography); }) .unwrap(), ); @@ -108,7 +113,7 @@ fn main() { thread::Builder::new() .name("Bob".to_string()) .spawn(move || { - bob_projector.epp_and_run(ParallelChoreography); + bob_projector.epp_and_run(MainChoreography); }) .unwrap(), ); @@ -116,7 +121,7 @@ fn main() { thread::Builder::new() .name("Carol".to_string()) .spawn(move || { - carol_projector.epp_and_run(ParallelChoreography); + carol_projector.epp_and_run(MainChoreography); }) .unwrap(), ); @@ -124,3 +129,64 @@ fn main() { handle.join().unwrap(); } } +#[cfg(test)] +mod tests { + use chorus_lib::core::Runner; + + use super::*; + + #[test] + fn test_projector() { + let transport_channel = LocalTransportChannelBuilder::new() + .with(Alice) + .with(Bob) + .with(Carol) + .build(); + let transport_alice = LocalTransport::new(Alice, transport_channel.clone()); + let transport_bob = LocalTransport::new(Bob, transport_channel.clone()); + let transport_carol = LocalTransport::new(Carol, transport_channel.clone()); + + let alice_projector = Projector::new(Alice, transport_alice); + let bob_projector = Projector::new(Bob, transport_bob); + let carol_projector = Projector::new(Carol, transport_carol); + + let mut handles: Vec> = Vec::new(); + handles.push( + thread::Builder::new() + .name("Alice".to_string()) + .spawn(move || { + alice_projector.epp_and_run(MainChoreography); + }) + .unwrap(), + ); + handles.push( + thread::Builder::new() + .name("Bob".to_string()) + .spawn(move || { + let v = bob_projector.epp_and_run(MainChoreography); + assert_eq!(bob_projector.unwrap(v.0), "Alice says hi to Bob"); + }) + .unwrap(), + ); + handles.push( + thread::Builder::new() + .name("Carol".to_string()) + .spawn(move || { + let v = carol_projector.epp_and_run(MainChoreography); + assert_eq!(carol_projector.unwrap(v.1), "Alice says hi to Carol"); + }) + .unwrap(), + ); + for handle in handles { + handle.join().unwrap(); + } + } + + #[test] + fn test_runner() { + let runner = Runner::new(); + let (v1, v2) = runner.run(MainChoreography); + assert_eq!(runner.unwrap(v1), "Alice says hi to Bob"); + assert_eq!(runner.unwrap(v2), "Alice says hi to Carol"); + } +} diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 74a01c7..b161666 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -667,11 +667,17 @@ where /// Construct a `Faceted` struct owned by the projection target. /// /// Use this method to run a choreography that takes a Faceted value as an input. - pub fn local_faceted(&self, value: V, _: Owners) -> Faceted - where Target: Member, + pub fn local_faceted( + &self, + value: V, + _: Owners, + ) -> Faceted + where + Target: Member, { - Faceted {value: HashMap::from([(String::from(Target::name()), value)]), - phantom: PhantomData, + Faceted { + value: HashMap::from([(String::from(Target::name()), value)]), + phantom: PhantomData, } } @@ -679,10 +685,12 @@ where /// /// Use this method to run a choreography that takes a Faceted value as an input. pub fn remote_faceted(&self, _: Owners) -> Faceted - where Owners: Subset<>::Remainder, Index2>, + where + Owners: Subset<>::Remainder, Index2>, { - Faceted {value: HashMap::new(), - phantom: PhantomData, + Faceted { + value: HashMap::new(), + phantom: PhantomData, } } @@ -1158,9 +1166,14 @@ impl Runner { /// Will almost certainly cause errors if you don't correctly populate the mapping. /// /// Use this method to run a choreography that takes a Faceted value as an input. - pub fn unsafe_faceted(&self, values: [(String, V); N], _: Owners) -> Faceted { - Faceted {value: HashMap::from(values), - phantom: PhantomData, + pub fn unsafe_faceted( + &self, + values: [(String, V); N], + _: Owners, + ) -> Faceted { + Faceted { + value: HashMap::from(values), + phantom: PhantomData, } } @@ -1294,14 +1307,71 @@ impl Runner { QSFoldable, >( &self, - _locations: QS, - _c: FOC, + _: QS, + c: FOC, ) -> Faceted where QS: Subset, QS: LocationSetFoldable, { - todo!() + let op = RunOp::(PhantomData); + let values = HashMap::new(); + + struct Loop< + ChoreoLS: LocationSet, + V, + QSSubsetL, + QS: LocationSet + Subset, + FOC: FanOutChoreography, + > { + phantom: PhantomData<(V, QSSubsetL, QS)>, + op: RunOp, + foc: FOC, + } + + impl< + ChoreoLS: LocationSet, + V, + QSSubsetL, + QS: LocationSet + Subset, + FOC: FanOutChoreography, + > LocationSetFolder> + for Loop + { + type L = ChoreoLS; + type QS = QS; + fn f( + &self, + mut acc: HashMap, + _: Q, + ) -> HashMap + where + Self::QS: Subset, + Q: Member, + Q: Member, + { + let v = self.foc.run::(&self.op); + match v.value { + Some(value) => { + acc.insert(String::from(Q::name()), value); + } + None => {} + }; + acc + } + } + let values = QS::foldr( + Loop:: { + phantom: PhantomData, + op, + foc: c, + }, + values, + ); + Faceted { + value: values, + phantom: PhantomData, + } } fn fanin< @@ -1319,15 +1389,78 @@ impl Runner { QSFoldable, >( &self, - _locations: QS, - _c: FIC, + _: QS, + c: FIC, ) -> MultiplyLocated, RS> where QS: Subset, RS: Subset, QS: LocationSetFoldable, { - todo!() + let op: RunOp = RunOp(PhantomData); + struct Loop< + ChoreoLS: LocationSet, + V, + QSSubsetL, + QS: LocationSet + Subset, + RSSubsetL, + RS: LocationSet + Subset, + FIC: FanInChoreography, + > { + phantom: PhantomData<(V, QSSubsetL, QS, RSSubsetL, RS)>, + op: RunOp, + fic: FIC, + } + impl< + ChoreoLS: LocationSet, + V, + QSSubsetL, + QS: LocationSet + Subset, + RSSubsetL, + RS: LocationSet + Subset, + FIC: FanInChoreography, + > LocationSetFolder> + for Loop + { + type L = ChoreoLS; + type QS = QS; + + fn f( + &self, + mut acc: HashMap, + _: Q, + ) -> HashMap + where + Self::QS: Subset, + Q: Member, + Q: Member, + { + let v = self + .fic + .run::(&self.op); + // if the target is in RS, `v` has a value (`Some`) + match v.value { + Some(value) => { + acc.insert(String::from(Q::name()), value); + } + None => {} + } + acc + } + } + let values = QS::foldr( + Loop:: { + phantom: PhantomData, + op, + fic: c, + }, + HashMap::new(), + ); + + MultiplyLocated::, RS>::local(Quire { + value: values, + phantom: PhantomData, + }) } } let op: RunOp = RunOp(PhantomData); From 35ed1bc3536ceb9fc79cb9a559ad0cbbc57a6f2b Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Thu, 31 Oct 2024 10:26:19 -0700 Subject: [PATCH 31/42] fix broken test --- chorus_lib/examples/bookseller.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/chorus_lib/examples/bookseller.rs b/chorus_lib/examples/bookseller.rs index e2c89f1..8eefa4b 100644 --- a/chorus_lib/examples/bookseller.rs +++ b/chorus_lib/examples/bookseller.rs @@ -3,10 +3,7 @@ extern crate chorus_lib; use std::io; use std::thread; -use chorus_lib::{ - core::{Located}, - transport::local::{LocalTransportChannelBuilder}, -}; +use chorus_lib::{core::Located, transport::local::LocalTransportChannelBuilder}; use chrono::NaiveDate; use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, LocationSet, Projector}; @@ -28,7 +25,7 @@ struct Seller; #[derive(ChoreographyLocation)] struct Buyer; -struct BooksellerChoreography{ +struct BooksellerChoreography { title: Located, budget: Located, } @@ -90,13 +87,13 @@ fn main() { let mut handles: Vec> = Vec::new(); handles.push(thread::spawn(move || { - seller_projector.epp_and_run(BooksellerChoreography{ + seller_projector.epp_and_run(BooksellerChoreography { title: seller_projector.remote(Buyer), budget: seller_projector.remote(Buyer), }); })); handles.push(thread::spawn(move || { - buyer_projector.epp_and_run(BooksellerChoreography{ + buyer_projector.epp_and_run(BooksellerChoreography { title: buyer_projector.local(title), budget: buyer_projector.local(BUDGET), }); @@ -112,7 +109,7 @@ mod tests { #[test] fn distributed_tapl() { - let title = String::new("TAPL"); + let title = String::from("TAPL"); let transport_channel = LocalTransportChannelBuilder::new() .with(Seller) @@ -126,19 +123,19 @@ mod tests { let mut handles: Vec> = Vec::new(); handles.push(thread::spawn(move || { - seller_projector.epp_and_run(BooksellerChoreography{ + seller_projector.epp_and_run(BooksellerChoreography { title: seller_projector.remote(Buyer), budget: seller_projector.remote(Buyer), }); })); handles.push(thread::spawn(move || { - buyer_projector.epp_and_run(BooksellerChoreography{ + buyer_projector.epp_and_run(BooksellerChoreography { title: buyer_projector.local(title), budget: buyer_projector.local(BUDGET), }); })); for h in handles { - assert!(h.join().unwrap()); + h.join().unwrap(); } } } From c30786be25f1271e95eb5088e07db8be6267b722 Mon Sep 17 00:00:00 2001 From: Mako Bates <33739499+ShapeOfMatter@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:47:14 -0500 Subject: [PATCH 32/42] census polymorphic kvs --- chorus_lib/tests/kvs.rs | 235 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 chorus_lib/tests/kvs.rs diff --git a/chorus_lib/tests/kvs.rs b/chorus_lib/tests/kvs.rs new file mode 100644 index 0000000..5279c34 --- /dev/null +++ b/chorus_lib/tests/kvs.rs @@ -0,0 +1,235 @@ +extern crate chorus_lib; + +use std::marker::PhantomData; +use std::thread; + +use chorus_lib::core::{ + ChoreoOp, Choreography, ChoreographyLocation, + Deserialize, + Faceted, + FanInChoreography, + HCons, HNil, + Located, LocationSet, + LocationSetFoldable, + Member, + MultiplyLocated, + Projector, + Portable, + Quire, + Serialize, + Subset, +}; +use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; + +type Response = i32; +type Key = String; + +#[derive(Serialize, Deserialize)] +enum Request { + Get(Key), + Put(Key, i32), +} + +fn handle_get(key: Key) -> Response { + key.len().try_into().unwrap() +} + +fn handle_put(key: Key, val: i32) -> Response { + (val != handle_get(key)) as Response +} + + +#[derive(ChoreographyLocation, Debug)] +struct Client; + +#[derive(ChoreographyLocation, Debug)] +struct Server; + +#[derive(ChoreographyLocation, Debug)] +struct Backup1; + +#[derive(ChoreographyLocation, Debug)] +struct Backup2; + +// This should perhaps be in core? +struct Gather<'a, + V, + Senders: LocationSet + Subset, + Recievers: LocationSet + Subset, + Census: LocationSet, + SendersPresent, + RecieversPresent> +{ + values: &'a Faceted, + phantom: PhantomData<(Census, SendersPresent, Recievers, RecieversPresent)>, +} +impl<'a, + V: Portable + Copy, + Senders: LocationSet + Subset, + Recievers: LocationSet + Subset, + Census: LocationSet, + SendersPresent, + RecieversPresent> + FanInChoreography for Gather<'a, V, Senders, Recievers, Census, SendersPresent, RecieversPresent> +{ + type L = Census; + type QS = Senders; + type RS = Recievers; + fn run( + &self, + op: &impl ChoreoOp, + ) -> MultiplyLocated + where + Self::QS: Subset, + Self::RS: Subset, + Sender: Member, + Sender: Member, + { + let x = op.locally(Sender::new(), |un| *un.unwrap3(&self.values)); + let x = op.multicast::( + Sender::new(), + ::new(), + &x, + ); + x + } +} + + +struct HandleRequest +{ + request: Located, + _phantoms: PhantomData<(Backups, BackupsPresent, BSpine)>, +} +impl + Choreography> for HandleRequest +where Backups: Subset, BackupsPresent>, + Backups: LocationSetFoldable, Backups, BSpine> +{ + type L = HCons; + fn run(self, op: &impl ChoreoOp) -> Located { + match op.broadcast(Server, self.request) { + Request::Put(key, value) => { + let oks = op.parallel(Backups::new(), ||{ handle_put(key.clone(), value) }); + let gathered = op.fanin::, _, _, _, _>( + Backups::new(), + Gather{values: &oks, phantom: PhantomData} + ); + op.locally(Server, |un| { + let ok = un.unwrap::, _, _>(&gathered).get_map().into_values().all(|response|{response == 0}); + if ok { + return handle_put(key.clone(), value) + } else { + return -1 + } + }) + } + Request::Get(key) => op.locally(Server, |_| { handle_get(key.clone()) }) + } + } +} + +struct KVS +{ + request: Located, + _phantoms: PhantomData<(Backups, BackupsPresent, BackupsAreServers, BSpine)>, +} +impl + Choreography> for KVS +where Backups: Subset>, BackupsPresent>, + Backups: Subset, BackupsAreServers>, + Backups: LocationSetFoldable, Backups, BSpine> +{ + type L = HCons>; + fn run(self, op: &impl ChoreoOp) -> Located { + let request = op.comm(Client, Server, &self.request); + let response = op.enclave(HandleRequest::{ + request: request, + _phantoms: PhantomData + }).flatten(); + op.comm(Server, Client, &response) + } +} + +fn run_test(request: Request, answer: Response) { + let transport_channel = LocalTransportChannelBuilder::new() + .with(Client) + .with(Server) + .with(Backup1) + .with(Backup2) + .build(); + let transport_client = LocalTransport::new(Client, transport_channel.clone()); + let transport_server = LocalTransport::new(Server, transport_channel.clone()); + let transport_backup1 = LocalTransport::new(Backup1, transport_channel.clone()); + let transport_backup2 = LocalTransport::new(Backup2, transport_channel.clone()); + + let client_projector = Projector::new(Client, transport_client); + let server_projector = Projector::new(Server, transport_server); + let backup1_projector = Projector::new(Backup1, transport_backup1); + let backup2_projector = Projector::new(Backup2, transport_backup2); + + let mut handles: Vec>> = Vec::new(); + handles.push( + thread::Builder::new() + .name("Client".to_string()) + .spawn(move || { + client_projector.epp_and_run(KVS::>, _, _, _>{ + request: client_projector.local(request), + _phantoms: PhantomData, + }) + }) + .unwrap(), + ); + handles.push( + thread::Builder::new() + .name("Server".to_string()) + .spawn(move || { + server_projector.epp_and_run(KVS::>, _, _, _>{ + request: server_projector.remote(Client), + _phantoms: PhantomData, + }) + }) + .unwrap(), + ); + handles.push( + thread::Builder::new() + .name("Backup1".to_string()) + .spawn(move || { + backup1_projector.epp_and_run(KVS::>, _, _, _>{ + request: backup1_projector.remote(Client), + _phantoms: PhantomData, + }) + }) + .unwrap(), + ); + handles.push( + thread::Builder::new() + .name("Backup2".to_string()) + .spawn(move || { + backup2_projector.epp_and_run(KVS::>, _, _, _>{ + request: backup2_projector.remote(Client), + _phantoms: PhantomData, + }) + }) + .unwrap(), + ); + let retval = Projector::new(Client, LocalTransport::new(Client, transport_channel.clone())) + .unwrap(handles.pop().unwrap().join().unwrap()); + for handle in handles { + handle.join().unwrap(); + } + assert_eq!(retval, answer); +} + +#[test] +fn main() { + let two = "xx".to_string(); + let three = "xxx".to_string(); + run_test(Request::Get(two.clone()), 2); + run_test(Request::Get(three.clone()), 3); + run_test(Request::Put(two.clone(), 2), 0); + run_test(Request::Put(three.clone(), 2), -1); +} From 2c1e810d442079e57bd45cd378f40c1a6c0fb035 Mon Sep 17 00:00:00 2001 From: Mako Bates <33739499+ShapeOfMatter@users.noreply.github.com> Date: Thu, 7 Nov 2024 14:15:35 -0500 Subject: [PATCH 33/42] fix answer extrations --- chorus_lib/tests/kvs.rs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/chorus_lib/tests/kvs.rs b/chorus_lib/tests/kvs.rs index 5279c34..22a13bc 100644 --- a/chorus_lib/tests/kvs.rs +++ b/chorus_lib/tests/kvs.rs @@ -172,17 +172,6 @@ fn run_test(request: Request, answer: Response) { let backup2_projector = Projector::new(Backup2, transport_backup2); let mut handles: Vec>> = Vec::new(); - handles.push( - thread::Builder::new() - .name("Client".to_string()) - .spawn(move || { - client_projector.epp_and_run(KVS::>, _, _, _>{ - request: client_projector.local(request), - _phantoms: PhantomData, - }) - }) - .unwrap(), - ); handles.push( thread::Builder::new() .name("Server".to_string()) @@ -216,12 +205,14 @@ fn run_test(request: Request, answer: Response) { }) .unwrap(), ); - let retval = Projector::new(Client, LocalTransport::new(Client, transport_channel.clone())) - .unwrap(handles.pop().unwrap().join().unwrap()); + let retval = client_projector.epp_and_run(KVS::>, _, _, _>{ + request: client_projector.local(request), + _phantoms: PhantomData, + }); for handle in handles { handle.join().unwrap(); } - assert_eq!(retval, answer); + assert_eq!(client_projector.unwrap(retval), answer); } #[test] From 66ff827c1a8c98f1f3f9a1b13d548c750736c9a4 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Wed, 13 Nov 2024 16:55:56 -0800 Subject: [PATCH 34/42] `cargo fmt` --- chorus_lib/examples/multicast.rs | 4 +- chorus_lib/tests/booksellers.rs | 328 +++++++++++++++++++------------ chorus_lib/tests/kvs.rs | 166 ++++++++-------- 3 files changed, 289 insertions(+), 209 deletions(-) diff --git a/chorus_lib/examples/multicast.rs b/chorus_lib/examples/multicast.rs index a376f7b..4c24913 100644 --- a/chorus_lib/examples/multicast.rs +++ b/chorus_lib/examples/multicast.rs @@ -2,9 +2,7 @@ extern crate chorus_lib; use std::thread; -use chorus_lib::core::{ - ChoreoOp, Choreography, ChoreographyLocation, LocationSet, Projector, -}; +use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, LocationSet, Projector}; use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; // --- Define two locations (Alice and Bob) --- diff --git a/chorus_lib/tests/booksellers.rs b/chorus_lib/tests/booksellers.rs index 7754b81..c5d3aaa 100644 --- a/chorus_lib/tests/booksellers.rs +++ b/chorus_lib/tests/booksellers.rs @@ -6,14 +6,10 @@ use std::sync::Arc; use std::thread; use chorus_lib::{ - core::{ChoreoOp, Choreography, ChoreographyLocation, Faceted, - FanInChoreography, - HCons, - Here, Located, LocationSet, - LocationSetFoldable, - Member, - Projector, - Quire, Runner, Subset, There}, + core::{ + ChoreoOp, Choreography, ChoreographyLocation, Faceted, FanInChoreography, HCons, Here, + Located, LocationSet, LocationSetFoldable, Member, Projector, Quire, Runner, Subset, There, + }, transport::local::{LocalTransport, LocalTransportChannelBuilder}, }; use chrono::NaiveDate; @@ -32,28 +28,35 @@ type Title = String; type Inventory = HashMap; - trait Decider { type Budgets; fn new(price: Located, Buyer1>, budgets: Self::Budgets) -> Self; } -struct Booksellers> + Decider, Budgets, Buyers, B1Index, BSIndex> { +struct Booksellers< + D: Choreography> + Decider, + Budgets, + Buyers, + B1Index, + BSIndex, +> { inventory: Located, title: Located, budgets: Budgets, _phantoms: PhantomData<(D, Buyers, B1Index, BSIndex)>, } -impl, L = Buyers> + Decider - ,Budgets - ,B1Index - ,BSIndex +impl< + Buyers: LocationSet, + D: Choreography, L = Buyers> + Decider, + Budgets, + B1Index, + BSIndex, > Choreography> for Booksellers - where Buyer1: Member - , Buyer1: Member, There> - , Buyers: Subset, BSIndex> +where + Buyer1: Member, + Buyer1: Member, There>, + Buyers: Subset, BSIndex>, { type L = HCons; fn run(self, op: &impl ChoreoOp) -> Option { @@ -61,7 +64,8 @@ impl; - fn new(price: Located, Buyer1>, budgets: Located) -> Self{ - return Self{price: price, budget: budgets} + fn new(price: Located, Buyer1>, budgets: Located) -> Self { + return Self { + price: price, + budget: budgets, + }; } } impl Choreography> for Unilateral { type L = LocationSet!(Buyer1); fn run(self, op: &impl ChoreoOp) -> Located { - op.locally(Buyer1, |un| { - match un.unwrap(&self.price) { - Some(price) => price <= un.unwrap(&self.budget), - None => false - } + op.locally(Buyer1, |un| match un.unwrap(&self.price) { + Some(price) => price <= un.unwrap(&self.budget), + None => false, }) } } - //////////////////////////////////////////////////////////////////////// struct Colaborative { price: Located, Buyer1>, budgets: Faceted, _phantoms: PhantomData<(B1Index, BSRefl, BSFld)>, } -impl Decider for Colaborative -where Buyer1: Member, - Buyers: Subset +impl Decider + for Colaborative +where + Buyer1: Member, + Buyers: Subset, { type Budgets = Faceted; - fn new(price: Located, Buyer1>, budgets: Faceted) -> Self{ - return Self{price: price, budgets: budgets, _phantoms: PhantomData} + fn new(price: Located, Buyer1>, budgets: Faceted) -> Self { + return Self { + price: price, + budgets: budgets, + _phantoms: PhantomData, + }; } } -impl - Choreography> for Colaborative -where Buyer1: Member, - Buyers: Subset, - Buyers: LocationSetFoldable +impl Choreography> + for Colaborative +where + Buyer1: Member, + Buyers: Subset, + Buyers: LocationSetFoldable, { type L = Buyers; fn run(self, op: &impl ChoreoOp) -> Located { match op.broadcast(Buyer1, self.price) { Some(price) => { - - struct Gather<'a, Buyers: LocationSet, B1Index> { - budgets: &'a Faceted, - _phantoms: PhantomData, - } - impl<'a, Buyers: LocationSet, B1Index> FanInChoreography for Gather<'a, Buyers, B1Index> - where Buyer1: Member + struct Gather<'a, Buyers: LocationSet, B1Index> { + budgets: &'a Faceted, + _phantoms: PhantomData, + } + impl<'a, Buyers: LocationSet, B1Index> FanInChoreography for Gather<'a, Buyers, B1Index> + where + Buyer1: Member, + { + type L = Buyers; + type QS = Buyers; + type RS = LocationSet!(Buyer1); + fn run( + &self, + op: &impl ChoreoOp, + ) -> Located + where + Self::QS: Subset, + Self::RS: Subset, + Q: Member, + Q: Member, { - type L = Buyers; - type QS = Buyers; - type RS = LocationSet!(Buyer1); - fn run( - &self, - op: &impl ChoreoOp, - ) -> Located - where - Self::QS: Subset, - Self::RS: Subset, - Q: Member, - Q: Member, - { - op.comm::<_, Q, Buyer1, Money, (QMemberL, _), QMemberL, B1Index>( - Q::new(), - Buyer1, - &op.locally::(Q::new(), - |un| *un.unwrap3::(&self.budgets)) - ) - } + op.comm::<_, Q, Buyer1, Money, (QMemberL, _), QMemberL, B1Index>( + Q::new(), + Buyer1, + &op.locally::(Q::new(), |un| { + *un.unwrap3::(&self.budgets) + }), + ) } - let budgets = op.fanin( - Buyers::new(), - Gather { - budgets: &self.budgets, - _phantoms: PhantomData, - }, - ); + } + let budgets = op.fanin( + Buyers::new(), + Gather { + budgets: &self.budgets, + _phantoms: PhantomData, + }, + ); - op.locally(Buyer1, |un| { - let budget = un.unwrap::, _, Here>(&budgets).get_map().into_values().sum(); - return price <= budget - }) - }, - None => op.locally(Buyer1, |_| {false}) + op.locally(Buyer1, |un| { + let budget = un + .unwrap::, _, Here>(&budgets) + .get_map() + .into_values() + .sum(); + return price <= budget; + }) + } + None => op.locally(Buyer1, |_| false), } } } - -fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option, answer: Option) { +fn run_test( + inventory: Inventory, + title: Title, + budget1: Money, + budget2: Option, + answer: Option, +) { let transport_channel = LocalTransportChannelBuilder::new() - .with(Seller).with(Buyer1).with(Buyer2) + .with(Seller) + .with(Buyer1) + .with(Buyer2) .build(); let seller_projector = Arc::new(Projector::new( Seller, @@ -215,17 +243,22 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< if let Some(budget2) = budget2 { { let central_runner = Runner::new(); - let choreo : Booksellers, - Faceted, - LocationSet!(Buyer1, Buyer2), - _, _> = Booksellers{ + let choreo: Booksellers< + Colaborative, + Faceted, + LocationSet!(Buyer1, Buyer2), + _, + _, + > = Booksellers { inventory: central_runner.local(inventory.clone()), title: central_runner.local(title.clone()), budgets: central_runner.unsafe_faceted( - [(String::from(Buyer1::name()), budget1), - (String::from(Buyer2::name()), budget2), + [ + (String::from(Buyer1::name()), budget1), + (String::from(Buyer2::name()), budget2), ], - ::new()), + ::new(), + ), _phantoms: PhantomData, }; let central_result = central_runner.run(choreo); @@ -233,10 +266,13 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } { handles.push(thread::spawn(move || { - let choreo : Booksellers, - Faceted, - LocationSet!(Buyer1, Buyer2), - _, _> = Booksellers{ + let choreo: Booksellers< + Colaborative, + Faceted, + LocationSet!(Buyer1, Buyer2), + _, + _, + > = Booksellers { inventory: seller_projector.local(inventory.clone()), title: seller_projector.remote(Buyer1), budgets: seller_projector.remote_faceted(::new()), @@ -247,13 +283,17 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } { handles.push(thread::spawn(move || { - let choreo : Booksellers, - Faceted, - LocationSet!(Buyer1, Buyer2), - _, _> = Booksellers{ + let choreo: Booksellers< + Colaborative, + Faceted, + LocationSet!(Buyer1, Buyer2), + _, + _, + > = Booksellers { inventory: buyer1_projector.remote(Seller), title: buyer1_projector.local(title).clone(), - budgets: buyer1_projector.local_faceted(budget1, ::new()), + budgets: buyer1_projector + .local_faceted(budget1, ::new()), _phantoms: PhantomData, }; buyer1_projector.epp_and_run(choreo) @@ -261,13 +301,17 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } { handles.push(thread::spawn(move || { - let choreo : Booksellers, - Faceted, - LocationSet!(Buyer1, Buyer2), - _, _> = Booksellers{ + let choreo: Booksellers< + Colaborative, + Faceted, + LocationSet!(Buyer1, Buyer2), + _, + _, + > = Booksellers { inventory: buyer2_projector.remote(Seller), title: buyer2_projector.remote(Buyer1), - budgets: buyer2_projector.local_faceted(budget2, ::new()), + budgets: buyer2_projector + .local_faceted(budget2, ::new()), _phantoms: PhantomData, }; buyer2_projector.epp_and_run(choreo) @@ -276,10 +320,13 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } else { { let central_runner = Runner::new(); - let choreo : Booksellers, - LocationSet!(Buyer1), - _, _> = Booksellers{ + let choreo: Booksellers< + Unilateral, + Located, + LocationSet!(Buyer1), + _, + _, + > = Booksellers { inventory: central_runner.local(inventory.clone()), title: central_runner.local(title.clone()), budgets: central_runner.local(budget1), @@ -290,10 +337,13 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } { handles.push(thread::spawn(move || { - let choreo : Booksellers, - LocationSet!(Buyer1), - _, _> = Booksellers{ + let choreo: Booksellers< + Unilateral, + Located, + LocationSet!(Buyer1), + _, + _, + > = Booksellers { inventory: seller_projector.local(inventory.clone()), title: seller_projector.remote(Buyer1), budgets: seller_projector.remote(Buyer1), @@ -304,10 +354,13 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } { handles.push(thread::spawn(move || { - let choreo : Booksellers, - LocationSet!(Buyer1), - _, _> = Booksellers{ + let choreo: Booksellers< + Unilateral, + Located, + LocationSet!(Buyer1), + _, + _, + > = Booksellers { inventory: buyer1_projector.remote(Seller), title: buyer1_projector.local(title).clone(), budgets: buyer1_projector.local(budget1), @@ -318,10 +371,13 @@ fn run_test(inventory: Inventory, title: Title, budget1: Money, budget2: Option< } { handles.push(thread::spawn(move || { - let choreo : Booksellers, - LocationSet!(Buyer1), - _, _> = Booksellers{ + let choreo: Booksellers< + Unilateral, + Located, + LocationSet!(Buyer1), + _, + _, + > = Booksellers { inventory: buyer2_projector.remote(Seller), title: buyer2_projector.remote(Buyer1), budgets: buyer2_projector.remote(Buyer1), @@ -353,10 +409,28 @@ fn main() { }; let tapl = "TAPL".to_string(); let hott = "HoTT".to_string(); - run_test(inventory.clone(), tapl.clone(), 100, None, Some(NaiveDate::from_ymd_opt(2023, 8, 3).unwrap())); + run_test( + inventory.clone(), + tapl.clone(), + 100, + None, + Some(NaiveDate::from_ymd_opt(2023, 8, 3).unwrap()), + ); run_test(inventory.clone(), hott.clone(), 25, None, None); - run_test(inventory.clone(), tapl.clone(), 30, Some(30), Some(NaiveDate::from_ymd_opt(2023, 8, 3).unwrap())); + run_test( + inventory.clone(), + tapl.clone(), + 30, + Some(30), + Some(NaiveDate::from_ymd_opt(2023, 8, 3).unwrap()), + ); run_test(inventory.clone(), hott.clone(), 30, Some(30), None); run_test(inventory.clone(), "nonesuch".to_string(), 25, None, None); - run_test(inventory.clone(), "nonesuch".to_string(), 30, Some(30), None); + run_test( + inventory.clone(), + "nonesuch".to_string(), + 30, + Some(30), + None, + ); } diff --git a/chorus_lib/tests/kvs.rs b/chorus_lib/tests/kvs.rs index 22a13bc..5d68e48 100644 --- a/chorus_lib/tests/kvs.rs +++ b/chorus_lib/tests/kvs.rs @@ -4,20 +4,9 @@ use std::marker::PhantomData; use std::thread; use chorus_lib::core::{ - ChoreoOp, Choreography, ChoreographyLocation, - Deserialize, - Faceted, - FanInChoreography, - HCons, HNil, - Located, LocationSet, - LocationSetFoldable, - Member, - MultiplyLocated, - Projector, - Portable, - Quire, - Serialize, - Subset, + ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Faceted, FanInChoreography, HCons, + HNil, Located, LocationSet, LocationSetFoldable, Member, MultiplyLocated, Portable, Projector, + Quire, Serialize, Subset, }; use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; @@ -38,7 +27,6 @@ fn handle_put(key: Key, val: i32) -> Response { (val != handle_get(key)) as Response } - #[derive(ChoreographyLocation, Debug)] struct Client; @@ -52,30 +40,39 @@ struct Backup1; struct Backup2; // This should perhaps be in core? -struct Gather<'a, - V, - Senders: LocationSet + Subset, - Recievers: LocationSet + Subset, - Census: LocationSet, - SendersPresent, - RecieversPresent> -{ +struct Gather< + 'a, + V, + Senders: LocationSet + Subset, + Recievers: LocationSet + Subset, + Census: LocationSet, + SendersPresent, + RecieversPresent, +> { values: &'a Faceted, phantom: PhantomData<(Census, SendersPresent, Recievers, RecieversPresent)>, } -impl<'a, - V: Portable + Copy, - Senders: LocationSet + Subset, - Recievers: LocationSet + Subset, - Census: LocationSet, - SendersPresent, - RecieversPresent> - FanInChoreography for Gather<'a, V, Senders, Recievers, Census, SendersPresent, RecieversPresent> +impl< + 'a, + V: Portable + Copy, + Senders: LocationSet + Subset, + Recievers: LocationSet + Subset, + Census: LocationSet, + SendersPresent, + RecieversPresent, + > FanInChoreography + for Gather<'a, V, Senders, Recievers, Census, SendersPresent, RecieversPresent> { type L = Census; type QS = Senders; type RS = Recievers; - fn run( + fn run< + Sender: ChoreographyLocation, + _SendersPresent, + _RecieversPresent, + SenderPresent, + SenderInSenders, + >( &self, op: &impl ChoreoOp, ) -> MultiplyLocated @@ -95,61 +92,67 @@ impl<'a, } } - -struct HandleRequest -{ +struct HandleRequest { request: Located, _phantoms: PhantomData<(Backups, BackupsPresent, BSpine)>, } -impl - Choreography> for HandleRequest -where Backups: Subset, BackupsPresent>, - Backups: LocationSetFoldable, Backups, BSpine> +impl Choreography> + for HandleRequest +where + Backups: Subset, BackupsPresent>, + Backups: LocationSetFoldable, Backups, BSpine>, { type L = HCons; fn run(self, op: &impl ChoreoOp) -> Located { match op.broadcast(Server, self.request) { Request::Put(key, value) => { - let oks = op.parallel(Backups::new(), ||{ handle_put(key.clone(), value) }); + let oks = op.parallel(Backups::new(), || handle_put(key.clone(), value)); let gathered = op.fanin::, _, _, _, _>( Backups::new(), - Gather{values: &oks, phantom: PhantomData} - ); + Gather { + values: &oks, + phantom: PhantomData, + }, + ); op.locally(Server, |un| { - let ok = un.unwrap::, _, _>(&gathered).get_map().into_values().all(|response|{response == 0}); - if ok { - return handle_put(key.clone(), value) - } else { - return -1 - } + let ok = un + .unwrap::, _, _>(&gathered) + .get_map() + .into_values() + .all(|response| response == 0); + if ok { + return handle_put(key.clone(), value); + } else { + return -1; + } }) } - Request::Get(key) => op.locally(Server, |_| { handle_get(key.clone()) }) + Request::Get(key) => op.locally(Server, |_| handle_get(key.clone())), } } } -struct KVS -{ +struct KVS { request: Located, _phantoms: PhantomData<(Backups, BackupsPresent, BackupsAreServers, BSpine)>, } impl - Choreography> for KVS -where Backups: Subset>, BackupsPresent>, - Backups: Subset, BackupsAreServers>, - Backups: LocationSetFoldable, Backups, BSpine> + Choreography> + for KVS +where + Backups: Subset>, BackupsPresent>, + Backups: Subset, BackupsAreServers>, + Backups: LocationSetFoldable, Backups, BSpine>, { type L = HCons>; fn run(self, op: &impl ChoreoOp) -> Located { let request = op.comm(Client, Server, &self.request); - let response = op.enclave(HandleRequest::{ - request: request, - _phantoms: PhantomData - }).flatten(); + let response = op + .enclave(HandleRequest:: { + request: request, + _phantoms: PhantomData, + }) + .flatten(); op.comm(Server, Client, &response) } } @@ -161,13 +164,13 @@ fn run_test(request: Request, answer: Response) { .with(Backup1) .with(Backup2) .build(); - let transport_client = LocalTransport::new(Client, transport_channel.clone()); - let transport_server = LocalTransport::new(Server, transport_channel.clone()); + let transport_client = LocalTransport::new(Client, transport_channel.clone()); + let transport_server = LocalTransport::new(Server, transport_channel.clone()); let transport_backup1 = LocalTransport::new(Backup1, transport_channel.clone()); let transport_backup2 = LocalTransport::new(Backup2, transport_channel.clone()); - let client_projector = Projector::new(Client, transport_client); - let server_projector = Projector::new(Server, transport_server); + let client_projector = Projector::new(Client, transport_client); + let server_projector = Projector::new(Server, transport_server); let backup1_projector = Projector::new(Backup1, transport_backup1); let backup2_projector = Projector::new(Backup2, transport_backup2); @@ -176,7 +179,7 @@ fn run_test(request: Request, answer: Response) { thread::Builder::new() .name("Server".to_string()) .spawn(move || { - server_projector.epp_and_run(KVS::>, _, _, _>{ + server_projector.epp_and_run(KVS::>, _, _, _> { request: server_projector.remote(Client), _phantoms: PhantomData, }) @@ -187,10 +190,12 @@ fn run_test(request: Request, answer: Response) { thread::Builder::new() .name("Backup1".to_string()) .spawn(move || { - backup1_projector.epp_and_run(KVS::>, _, _, _>{ - request: backup1_projector.remote(Client), - _phantoms: PhantomData, - }) + backup1_projector.epp_and_run( + KVS::>, _, _, _> { + request: backup1_projector.remote(Client), + _phantoms: PhantomData, + }, + ) }) .unwrap(), ); @@ -198,17 +203,20 @@ fn run_test(request: Request, answer: Response) { thread::Builder::new() .name("Backup2".to_string()) .spawn(move || { - backup2_projector.epp_and_run(KVS::>, _, _, _>{ - request: backup2_projector.remote(Client), - _phantoms: PhantomData, - }) + backup2_projector.epp_and_run( + KVS::>, _, _, _> { + request: backup2_projector.remote(Client), + _phantoms: PhantomData, + }, + ) }) .unwrap(), ); - let retval = client_projector.epp_and_run(KVS::>, _, _, _>{ - request: client_projector.local(request), - _phantoms: PhantomData, - }); + let retval = + client_projector.epp_and_run(KVS::>, _, _, _> { + request: client_projector.local(request), + _phantoms: PhantomData, + }); for handle in handles { handle.join().unwrap(); } From 5df26670273a52b2cdd1dd2899f71bb17627f730 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Wed, 13 Nov 2024 17:09:21 -0800 Subject: [PATCH 35/42] describe multiply located values --- chorus_book/src/guide-located-values.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/chorus_book/src/guide-located-values.md b/chorus_book/src/guide-located-values.md index c6258e6..73c2759 100644 --- a/chorus_book/src/guide-located-values.md +++ b/chorus_book/src/guide-located-values.md @@ -2,20 +2,28 @@ As we have seen in the [Choreography](./guide-choreography.md) section, a located value is a value that is available only at a specific location. In this section, we will discuss located values in more detail. -## `Located` struct +## `MultiplyLocated` struct -The `Located` struct represents a located value. It is a generic struct that takes two type parameters: a type parameter `V` that represents the type of the value, and a type parameter `L1` that represents the location where the value is available. +The `MultiplyLocated` struct represents a located value that is available at multiple locations. It is a generic struct that takes two type parameters: a type parameter `V` that represents the type of the value, and a type parameter `L` that represents the location set where the value is available. ```rust,ignore -pub struct Located +pub struct MultiplyLocated where - L1: ChoreographyLocation, + L: LocationSet, { // ... } ``` -The `Located` struct can be in one of the two states: `Local` and `Remote`. The `Local` state represents a located value that is available at the current location. The `Remote` state represents a located value that is available at a different location. +The `MultiplyLocated` struct can be in one of the two states: `Local` and `Remote`. The `Local` state represents a located value that is available at the current location (that is, the current location is a member of the location set `L`). The `Remote` state represents a located value that is available at a different location. + +## `Located` struct + +In some cases, we may want to represent a located value that is available at a single location. For example, the return value of the `locally` operator is a (singly) located value that is available at the location given as an argument to the operator. The `Located` is used to represent such located values. It is a type alias for the `MultiplyLocated` struct with the location set containing only one location. + +```rust,ignore +type Located = MultiplyLocated; +``` ## `Portable` trait From ffbfdb359891dfacb7f20e5358da754c8fa695ba Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Sat, 23 Nov 2024 22:51:04 -0800 Subject: [PATCH 36/42] use `unwrap` to unwrap both MLV and Quire --- chorus_lib/examples/cardgame.rs | 13 +++++----- chorus_lib/examples/fanout.rs | 4 +-- chorus_lib/examples/parallel.rs | 6 ++--- chorus_lib/src/core.rs | 46 ++++++++++++++++++++++++++------- chorus_lib/tests/booksellers.rs | 4 +-- chorus_lib/tests/kvs.rs | 6 ++--- 6 files changed, 52 insertions(+), 27 deletions(-) diff --git a/chorus_lib/examples/cardgame.rs b/chorus_lib/examples/cardgame.rs index c4d3701..a5a6746 100644 --- a/chorus_lib/examples/cardgame.rs +++ b/chorus_lib/examples/cardgame.rs @@ -115,7 +115,7 @@ impl< Q: Member, Q: Member, { - let x = op.locally(Q::new(), |un| *un.unwrap3(&self.hand1)); + let x = op.locally(Q::new(), |un| *un.unwrap(self.hand1)); let x = op.multicast::( Q::new(), ::new(), @@ -150,8 +150,8 @@ impl< Q: Member, { op.locally(Q::new(), |un| { - let hand1 = *un.unwrap3(&self.hand1); - let on_the_table = un.unwrap(&self.on_the_table); + let hand1 = *un.unwrap(self.hand1); + let on_the_table = un.unwrap(self.on_the_table); println!("My first card is: {}", hand1); println!("On the table: {:?}", on_the_table); println!("I'll ask for another? [True/False]"); @@ -193,7 +193,6 @@ impl< Q: Member, Q: Member, { - // TODO: enclave struct Enclave { hand1: Located, wants_next_card: Located, @@ -218,8 +217,8 @@ impl< } } } - let hand1 = op.locally(Q::new(), |un| *un.unwrap3(self.hand1)); - let wants_next_card = op.locally(Q::new(), |un| *un.unwrap3(self.wants_next_card)); + let hand1 = op.locally(Q::new(), |un| *un.unwrap(self.hand1)); + let wants_next_card = op.locally(Q::new(), |un| *un.unwrap(self.wants_next_card)); op.enclave(Enclave:: { hand1, wants_next_card, @@ -257,7 +256,7 @@ impl< Q: Member, { op.locally(Q::new(), |un| { - let mut hand2 = un.unwrap3(self.hand2).clone(); + let mut hand2 = un.unwrap(self.hand2).clone(); hand2.push(self.table_card); println!("Final hands: {:?}", hand2); let sum: i32 = hand2.iter().sum(); diff --git a/chorus_lib/examples/fanout.rs b/chorus_lib/examples/fanout.rs index a73d25a..0e3947e 100644 --- a/chorus_lib/examples/fanout.rs +++ b/chorus_lib/examples/fanout.rs @@ -73,12 +73,12 @@ impl Choreography<(Located, Located)> for MainChoreo fn run(self, op: &impl ChoreoOp) -> (Located, Located) { let v = op.fanout(::new(), FanOut::new(Alice)); let value_at_bob = op.locally(Bob, |un| { - let v = un.unwrap3(&v); + let v = un.unwrap(&v); println!("{}", v); v.clone() }); let value_at_carol = op.locally(Carol, |un| { - let v = un.unwrap3(&v); + let v = un.unwrap(&v); println!("{}", v); v.clone() }); diff --git a/chorus_lib/examples/parallel.rs b/chorus_lib/examples/parallel.rs index e452a8f..3c21077 100644 --- a/chorus_lib/examples/parallel.rs +++ b/chorus_lib/examples/parallel.rs @@ -25,15 +25,15 @@ impl Choreography for ParallelChoreography { rand::thread_rng().gen_range(1..=10) }); op.locally(Alice, |un| { - let x = un.unwrap3(&faceted); + let x = un.unwrap(&faceted); println!("Alice picked {}", x); }); op.locally(Bob, |un| { - let x = un.unwrap3(&faceted); + let x = un.unwrap(&faceted); println!("Bob picked {}", x); }); op.locally(Carol, |un| { - let x = un.unwrap3(&faceted); + let x = un.unwrap(&faceted); println!("Carol picked {}", x); }); } diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index b161666..60835ec 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -186,6 +186,38 @@ where phantom: PhantomData, } +/// Represents a value that can be unwrapped at any location in `L` +pub trait Unwrappable<'a, V> { + /// A location set that the value is located at + type L: LocationSet; + /// Unwraps a located value at a given location + fn unwrap_at(&'a self, location: L1) -> &'a V + where + L1: Member; +} + +impl<'a, V, L: LocationSet> Unwrappable<'a, V> for MultiplyLocated { + type L = L; + + fn unwrap_at(&'a self, _: L1) -> &'a V + where + L1: Member, + { + self.value.as_ref().unwrap() + } +} + +impl<'a, V, L: LocationSet> Unwrappable<'a, V> for Faceted { + type L = L; + + fn unwrap_at(&'a self, _: L1) -> &'a V + where + L1: Member, + { + self.value.get(&L1::name().to_string()).unwrap() + } +} + // --- HList and Helpers --- /// xx @@ -374,19 +406,13 @@ pub struct Unwrapper { } impl Unwrapper { - /// TODO: documentation - pub fn unwrap<'a, V, S: LocationSet, Index>(&self, mlv: &'a MultiplyLocated) -> &'a V - where - L1: Member, - { - mlv.value.as_ref().unwrap() - } - /// TODO: documentation - pub fn unwrap3<'a, V, S: LocationSet, Index>(&self, faceted: &'a Faceted) -> &'a V + /// Unwraps a located value at the current location + pub fn unwrap<'a, V, S: LocationSet, Index, U>(&self, unwrappable: &'a U) -> &'a V where + U: Unwrappable<'a, V, L = S> + 'a, L1: Member, { - faceted.value.get(L1::name()).unwrap() + unwrappable.unwrap_at::(L1::new()) } } diff --git a/chorus_lib/tests/booksellers.rs b/chorus_lib/tests/booksellers.rs index c5d3aaa..cc30349 100644 --- a/chorus_lib/tests/booksellers.rs +++ b/chorus_lib/tests/booksellers.rs @@ -187,7 +187,7 @@ where Q::new(), Buyer1, &op.locally::(Q::new(), |un| { - *un.unwrap3::(&self.budgets) + *un.unwrap::<_, _, QMemberQS, _>(self.budgets) }), ) } @@ -202,7 +202,7 @@ where op.locally(Buyer1, |un| { let budget = un - .unwrap::, _, Here>(&budgets) + .unwrap::, _, Here, _>(&budgets) .get_map() .into_values() .sum(); diff --git a/chorus_lib/tests/kvs.rs b/chorus_lib/tests/kvs.rs index 5d68e48..959990d 100644 --- a/chorus_lib/tests/kvs.rs +++ b/chorus_lib/tests/kvs.rs @@ -6,7 +6,7 @@ use std::thread; use chorus_lib::core::{ ChoreoOp, Choreography, ChoreographyLocation, Deserialize, Faceted, FanInChoreography, HCons, HNil, Located, LocationSet, LocationSetFoldable, Member, MultiplyLocated, Portable, Projector, - Quire, Serialize, Subset, + Serialize, Subset, }; use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; @@ -82,7 +82,7 @@ impl< Sender: Member, Sender: Member, { - let x = op.locally(Sender::new(), |un| *un.unwrap3(&self.values)); + let x = op.locally(Sender::new(), |un| *un.unwrap(self.values)); let x = op.multicast::( Sender::new(), ::new(), @@ -116,7 +116,7 @@ where ); op.locally(Server, |un| { let ok = un - .unwrap::, _, _>(&gathered) + .unwrap(&gathered) .get_map() .into_values() .all(|response| response == 0); From 2339c94127336599457682f0ba1ab095ee17c0a7 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Thu, 28 Nov 2024 14:20:57 -0800 Subject: [PATCH 37/42] explain `multicast` --- chorus_book/src/guide-choreography.md | 37 +++++++++++++++++++++++++ chorus_book/src/guide-located-values.md | 2 +- chorus_book/src/guide-locations.md | 15 ++++++++++ chorus_book/src/header.txt | 2 +- 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/chorus_book/src/guide-choreography.md b/chorus_book/src/guide-choreography.md index 1480948..22e0738 100644 --- a/chorus_book/src/guide-choreography.md +++ b/chorus_book/src/guide-choreography.md @@ -161,6 +161,43 @@ if num == 42 { } ``` +### Multicast + +The `multicast` operator is similar to the `broadcast` operator, but it allows you to manually specify the recipients instead of sending the value to all locations. The operator returns a `MultiplyLocated` value that is available at the all recipient locations. + +```rust +{{#include ./header.txt}} +# +# struct HelloWorldChoreography; +# impl Choreography for HelloWorldChoreography { +# type L = LocationSet!(Alice, Bob, Carol); +# fn run(self, op: &impl ChoreoOp) { +// This value is only available at Alice +let num_at_alice: Located = op.locally(Alice, |_| { + 42 +}); + +// Send the value from Alice to Bob and Carol +let num_at_bob_and_carol: MultiplyLocated = + op.multicast(Alice, ::new(), &num_at_alice); + +// Bob and Carol can now access the value +op.locally(Bob, |un| { + let num_at_bob: &i32 = un.unwrap(&num_at_bob_and_carol); + println!("The number at Bob is {}", num_at_bob); +}); +op.locally(Carol, |un| { + let num_at_carol: &i32 = un.unwrap(&num_at_bob_and_carol); + println!("The number at Carol is {}", num_at_carol); +}); +# } +# } +``` + +The second parameter of the `multicast` is a value of the `LocationSet` type. You can use the `new()` method of the `LocationSet` type to obtain a value representation of the location set. + +Both `Bob` and `Carol` can access the value sent from `Alice` inside their local computation using the same `unwrap` method. + ### Note on invalid values for Choreography::L You'll get a compile error if you try to work with a `ChoreographyLocation` that is not a member of `L`. diff --git a/chorus_book/src/guide-located-values.md b/chorus_book/src/guide-located-values.md index 73c2759..648cbb4 100644 --- a/chorus_book/src/guide-located-values.md +++ b/chorus_book/src/guide-located-values.md @@ -17,7 +17,7 @@ where The `MultiplyLocated` struct can be in one of the two states: `Local` and `Remote`. The `Local` state represents a located value that is available at the current location (that is, the current location is a member of the location set `L`). The `Remote` state represents a located value that is available at a different location. -## `Located` struct +## `Located` type In some cases, we may want to represent a located value that is available at a single location. For example, the return value of the `locally` operator is a (singly) located value that is available at the location given as an argument to the operator. The `Located` is used to represent such located values. It is a type alias for the `MultiplyLocated` struct with the location set containing only one location. diff --git a/chorus_book/src/guide-locations.md b/chorus_book/src/guide-locations.md index f26ffe9..0d109b5 100644 --- a/chorus_book/src/guide-locations.md +++ b/chorus_book/src/guide-locations.md @@ -46,3 +46,18 @@ use chorus_lib::core::LocationSet; type L = LocationSet!(Alice, Bob); ``` + +Some operators, such as `multicast`, requires a value of `LocationSet` to be passed as an argument. You can obtain a value of `LocationSet` by using the `new` method. + +```rust +# extern crate chorus_lib; +# use chorus_lib::core::ChoreographyLocation; +# #[derive(ChoreographyLocation)] +# struct Alice; +# +# #[derive(ChoreographyLocation)] +# struct Bob; +# use chorus_lib::core::LocationSet; +# +let alice_and_bob = ::new(); +``` diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index f24bfe6..f995e9d 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -1,5 +1,5 @@ # extern crate chorus_lib; -# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, Superposition, Runner, LocationSet}; +# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, MultiplyLocated, Superposition, Runner, LocationSet}; # use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; # #[derive(ChoreographyLocation)] # struct Alice; From 16559b4bf3fc29c3015c57ac48da905b1a6b25e4 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Sat, 30 Nov 2024 12:51:13 -0800 Subject: [PATCH 38/42] rewrite efficient conditionals chapter --- Cargo.toml | 1 + chorus_book/book.toml | 3 + chorus_book/src/SUMMARY.md | 2 +- .../src/guide-efficient-conditionals.md | 302 ++++++++++++++++++ chorus_book/src/guide-enclave.md | 115 ------- chorus_book/src/guide-located-values.md | 2 +- chorus_book/src/guide-locations.md | 2 +- chorus_book/src/header.txt | 2 +- 8 files changed, 310 insertions(+), 119 deletions(-) create mode 100644 chorus_book/src/guide-efficient-conditionals.md delete mode 100644 chorus_book/src/guide-enclave.md diff --git a/Cargo.toml b/Cargo.toml index db37e0e..154fe08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] +edition = "2021" members = ["chorus_lib", "chorus_derive"] resolver = "2" diff --git a/chorus_book/book.toml b/chorus_book/book.toml index 61adcbf..83c4c93 100644 --- a/chorus_book/book.toml +++ b/chorus_book/book.toml @@ -5,6 +5,9 @@ multilingual = false src = "src" title = "ChoRus" +[rust] +edition = "2021" + [output.html] git-repository-url = "https://github.com/lsd-ucsc/Chorus" git-repository-icon = "fa-github" diff --git a/chorus_book/src/SUMMARY.md b/chorus_book/src/SUMMARY.md index 222c1b0..8aab5e3 100644 --- a/chorus_book/src/SUMMARY.md +++ b/chorus_book/src/SUMMARY.md @@ -12,6 +12,6 @@ - [Input and Output](./guide-input-and-output.md) - [Higher-order Choreography](./guide-higher-order-choreography.md) - [Location Polymorphism](./guide-location-polymorphism.md) - - [Choreographic Enclave and Efficient Conditional](./guide-enclave.md) + - [Efficient Conditionals with Enclaves and MLVs](./guide-efficient-conditionals.md) - [Runner](./guide-runner.md) - [Links](./links.md) diff --git a/chorus_book/src/guide-efficient-conditionals.md b/chorus_book/src/guide-efficient-conditionals.md new file mode 100644 index 0000000..ab56880 --- /dev/null +++ b/chorus_book/src/guide-efficient-conditionals.md @@ -0,0 +1,302 @@ +# Efficient Conditionals with Enclaves and MLVs + +## `broadcast` incurs unnecessary communication + +In [the previous section](./guide-choreography.html#broadcast), we discussed how the `broadcast` operator can be used to implement a conditional behavior in a choreography. In short, the `broadcast` operator sends a located value from a source location to all other locations, making the value available at all locations. The resulting value is a normal (not `Located`) value and it can be used to make a branch. + +However, the `broadcast` operator can incur unnecessary communication when not all locations need to receive the value. Consider a simple key-value store where a *client* sends either a `Get` or `Put` request to a *primary* server, and the primary server forwards the request to a *backup* server if the request is a `Put`. The backup server does not need to receive the request if the request is a `Get`. + +Using the `broadcast` operator, this protocol can be implemented as follows: + +```rust +{{#include ./header.txt}} +# +# fn read_request() -> Request { +# Request::Put("key".to_string(), "value".to_string()) +# } +# fn get_value(key: &Key) -> Option { +# Some("value".to_string()) +# } +# fn set_value(key: &Key, value: &Value) { +# println!("Saved key: {} and value: {}", key, value); +# } +# +#[derive(ChoreographyLocation)] +struct Client; + +#[derive(ChoreographyLocation)] +struct Primary; + +#[derive(ChoreographyLocation)] +struct Backup; + +type Key = String; +type Value = String; + +#[derive(Serialize, Deserialize)] +enum Request { + Get(Key), + Put(Key, Value), +} + +#[derive(Serialize, Deserialize)] +enum Response { + GetOk(Option), + PutOk, +} + +struct KeyValueStoreChoreography; + +impl Choreography> for KeyValueStoreChoreography { + type L = LocationSet!(Client, Primary, Backup); + fn run(self, op: &impl ChoreoOp) -> Located { + // Read the request from the client + let request_at_client: Located = op.locally(Client, |_| read_request()); + // Send the request to the primary server + let request_at_primary: Located = + op.comm(Client, Primary, &request_at_client); + // Check if the request is a `Put` + let is_put_at_primary: Located = op.locally(Primary, |un| { + matches!(un.unwrap(&request_at_primary), Request::Put(_, _)) + }); + // Broadcast the `is_put_at_primary` to all locations so it can be used for branching + let is_put: bool = op.broadcast(Primary, is_put_at_primary); // <-- Incurs unnecessary communication + // Depending on the request, set or get the value + let response_at_primary = if is_put { + let request_at_backup: Located = + op.comm(Primary, Backup, &request_at_primary); + op.locally(Backup, |un| match un.unwrap(&request_at_backup) { + Request::Put(key, value) => set_value(key, value), + _ => (), + }); + op.locally(Primary, |_| Response::PutOk) + } else { + op.locally(Primary, |un| { + let key = match un.unwrap(&request_at_primary) { + Request::Get(key) => key, + _ => &"".to_string(), + }; + Response::GetOk(get_value(key)) + }) + }; + // Send the response from the primary to the client + let response_at_client = op.comm(Primary, Client, &response_at_primary); + response_at_client + } +} +``` + +While this implementation works, it incurs unnecessary communication. When we branch on `is_put`, we broadcast the value to all locations. This is necessary to make sure that the value is available at all locations so it can be used as a normal, non-located value. However, notice that the client does not need to receive the value. Regardless of whether the request is a `Put` or `Get`, the client should wait for the response from the primary server. + +## Changing the census with `enclave` + +To avoid unnecessary communication, we can use the `enclave` operator. The `enclave` operator is similar to [the `call` operator](./guide-higher-order-choreography.html) but executes a sub-choreography only at locations that are included in its location set. Inside the sub-choreography, `broadcast` only sends the value to the locations that are included in the location set. This allows us to avoid unnecessary communication. + +Let's refactor the previous example using the `enclave` operator. We define a sub-choreography `HandleRequestChoreography` that describes how the primary and backup servers (but not the client) handle the request and use the `enclave` operator to execute the sub-choreography. + +```rust +{{#include ./header.txt}} +# +# fn read_request() -> Request { +# Request::Put("key".to_string(), "value".to_string()) +# } +# fn get_value(key: &Key) -> Option { +# Some("value".to_string()) +# } +# fn set_value(key: &Key, value: &Value) { +# println!("Saved key: {} and value: {}", key, value); +# } +# +# #[derive(ChoreographyLocation)] +# struct Client; +# +# #[derive(ChoreographyLocation)] +# struct Primary; +# +# #[derive(ChoreographyLocation)] +# struct Backup; +# +# type Key = String; +# type Value = String; +# +# #[derive(Serialize, Deserialize)] +# enum Request { +# Get(Key), +# Put(Key, Value), +# } +# +# #[derive(Serialize, Deserialize)] +# enum Response { +# GetOk(Option), +# PutOk, +# } +# +struct HandleRequestChoreography { + request: Located, +} + +// This sub-choreography describes how the primary and backup servers handle the request +impl Choreography> for HandleRequestChoreography { + type L = LocationSet!(Primary, Backup); + fn run(self, op: &impl ChoreoOp) -> Located { + let is_put_request: Located = op.locally(Primary, |un| { + matches!(un.unwrap(&self.request), Request::Put(_, _)) + }); + let is_put: bool = op.broadcast(Primary, is_put_request); + let response_at_primary = if is_put { + let request_at_backup: Located = + op.comm(Primary, Backup, &self.request); + op.locally(Backup, |un| match un.unwrap(&request_at_backup) { + Request::Put(key, value) => set_value(key, value), + _ => (), + }); + op.locally(Primary, |_| Response::PutOk) + } else { + op.locally(Primary, |un| { + let key = match un.unwrap(&self.request) { + Request::Get(key) => key, + _ => &"".to_string(), + }; + Response::GetOk(get_value(key)) + }) + }; + response_at_primary + } +} + +struct KeyValueStoreChoreography; + +impl Choreography> for KeyValueStoreChoreography { + type L = LocationSet!(Client, Primary, Backup); + fn run(self, op: &impl ChoreoOp) -> Located { + let request_at_client: Located = op.locally(Client, |_| read_request()); + let request_at_primary: Located = + op.comm(Client, Primary, &request_at_client); + // Execute the sub-choreography only at the primary and backup servers + let response: MultiplyLocated, LocationSet!(Primary, Backup)> = + op.enclave(HandleRequestChoreography { + request: request_at_primary, + }); + let response_at_primary: Located = response.flatten(); + let response_at_client = op.comm(Primary, Client, &response_at_primary); + response_at_client + } +} +``` + +In this refactored version, the `HandleRequestChoreography` sub-choreography describes how the primary and backup servers handle the request. The `enclave` operator executes the sub-choreography only at the primary and backup servers. The `broadcast` operator inside the sub-choreography sends the value only to the primary and backup servers, avoiding unnecessary communication. + +The `enclave` operator returns a return value of the sub-choreography wrapped as a `MultiplyLocated` value. Since `HandleRequestChoreography` returns a `Located`, the return value of the `enclave` operator is a `MultiplyLocated, LocationSet!(Primary, Backup)>`. To get the located value at the primary server, we can use the `locally` operator to unwrap the `MultiplyLocated` value on the primary. Since this is a common pattern, we provide the `flatten` method on `MultiplyLocated` to simplify this operation. + +With the `enclave` operator, we can avoid unnecessary communication and improve the efficiency of the choreography. + +## Reusing Knowledge of Choice in Enclaves + +The key idea behind the `enclave` operator is that a normal value inside a choreography is equivalent to a (multiply) located value at all locations executing the choreography. This is why a normal value in a sub-choreography becomes a multiply located value at all locations executing the sub-choreography when returned from the `enclave` operator. + +It is possible to perform this conversion in the opposite direction as well. If we have a multiply located value at some locations, and those are the only locations executing the choreography, then we can obtain a normal value out of the multiply located value. This is useful when we want to reuse the already known information about a choice in an enclave. + +Inside a choreography, we can use the `naked` operator to convert a multiply located value at locations `S` to a normal value if the census of the choreography is a subset of `S`. + +For example, the above choreography can be written as follows: + +```rust +{{#include ./header.txt}} +# +# fn read_request() -> Request { +# Request::Put("key".to_string(), "value".to_string()) +# } +# fn get_value(key: &Key) -> Option { +# Some("value".to_string()) +# } +# fn set_value(key: &Key, value: &Value) { +# println!("Saved key: {} and value: {}", key, value); +# } +# +# #[derive(ChoreographyLocation)] +# struct Client; +# +# #[derive(ChoreographyLocation)] +# struct Primary; +# +# #[derive(ChoreographyLocation)] +# struct Backup; +# +# type Key = String; +# type Value = String; +# +# #[derive(Serialize, Deserialize)] +# enum Request { +# Get(Key), +# Put(Key, Value), +# } +# +# #[derive(Serialize, Deserialize)] +# enum Response { +# GetOk(Option), +# PutOk, +# } +# +struct HandleRequestChoreography { + request: Located, + is_put: MultiplyLocated, +} + +impl Choreography> for HandleRequestChoreography { + type L = LocationSet!(Primary, Backup); + fn run(self, op: &impl ChoreoOp) -> Located { + // obtain a normal boolean because {Primary, Backup} is the census of the choreography + let is_put: bool = op.naked(self.is_put); + let response_at_primary = if is_put { + // ... +# let request_at_backup: Located = +# op.comm(Primary, Backup, &self.request); +# op.locally(Backup, |un| match un.unwrap(&request_at_backup) { +# Request::Put(key, value) => set_value(key, value), +# _ => (), +# }); +# op.locally(Primary, |_| Response::PutOk) + } else { + // ... +# op.locally(Primary, |un| { +# let key = match un.unwrap(&self.request) { +# Request::Get(key) => key, +# _ => &"".to_string(), +# }; +# Response::GetOk(get_value(key)) +# }) + }; + response_at_primary + } +} + +struct KeyValueStoreChoreography; + +impl Choreography> for KeyValueStoreChoreography { + type L = LocationSet!(Client, Primary, Backup); + fn run(self, op: &impl ChoreoOp) -> Located { + let request_at_client: Located = op.locally(Client, |_| read_request()); + let request_at_primary: Located = + op.comm(Client, Primary, &request_at_client); + let is_put_at_primary: Located = op.locally(Primary, |un| { + matches!(un.unwrap(&request_at_primary), Request::Put(_, _)) + }); + // get a MLV by multicasting the boolean to the census of the sub-choreography + let is_put: MultiplyLocated = op.multicast( + Primary, + ::new(), + &is_put_at_primary, + ); + let response: MultiplyLocated, LocationSet!(Primary, Backup)> = + op.enclave(HandleRequestChoreography { + is_put, + request: request_at_primary, + }); + let response_at_primary: Located = response.flatten(); + let response_at_client = op.comm(Primary, Client, &response_at_primary); + response_at_client + } +} +``` + +In this version, we first `multicast` the boolean value to the census of the sub-choreography (`Primary` and `Client`) and we pass the MLV to the sub-choreography. Inside the sub-choreography, we use the `naked` operator to obtain a normal boolean value. This allows us to reuse the already known information about the choice in the sub-choreography. diff --git a/chorus_book/src/guide-enclave.md b/chorus_book/src/guide-enclave.md deleted file mode 100644 index f14f9b7..0000000 --- a/chorus_book/src/guide-enclave.md +++ /dev/null @@ -1,115 +0,0 @@ -# Choreographic Enclave and Efficient Conditional - -ChoRus supports the `enclave` operator to achieve efficient conditional execution. - -## Conditional with Broadcast - -Consider the following protocol: - -1. Alice generates a random number `x` and sends it to Bob. -2. Bob checks if `x` is even. If it is even, Bob sends `x` to Carol. Otherwise, Bob terminates. - -This protocol can be implemented as follows: - -```rust -{{#include ./header.txt}} -# fn get_random_number() -> u32 { -# 42 // for presentation purpose -# } -# -struct DemoChoreography; - -impl Choreography for DemoChoreography { - type L = LocationSet!(Alice, Bob, Carol); - fn run(self, op: &impl ChoreoOp) { - let x_at_alice = op.locally(Alice, |_| { - get_random_number() - }); - let x_at_bob = op.comm(Alice, Bob, &x_at_alice); - let is_even_at_bob: Located = op.locally(Bob, |un| { - let x = un.unwrap(&x_at_bob); - x % 2 == 0 - }); - let is_even: bool = op.broadcast(Bob, is_even_at_bob); - if is_even { - let x_at_carol = op.comm(Bob, Carol, &x_at_bob); - op.locally(Carol, |un| { - let x = un.unwrap(&x_at_carol); - println!("x is even: {}", x); - }); - } - } -} -``` - -While this code correctly implements the protocol, it is inefficient. The `is_even` value is broadcasted to all locations, but Alice does not need to receive the value. Ideally, we want to send `is_even_at_bob` only to Carol and branch only on Bob and Carol. - -In ChoRus, we can achieve this using the `enclave` operator. First, let us define a sub-choreography that describes the communication between Bob and Carol: - -```rust -{{#include ./header.txt}} -struct BobCarolChoreography { - x_at_bob: Located, -}; -impl Choreography for BobCarolChoreography { - type L = LocationSet!(Bob, Carol); - fn run(self, op: &impl ChoreoOp) { - let is_even_at_bob: Located = op.locally(Bob, |un| { - let x = un.unwrap(&self.x_at_bob); - x % 2 == 0 - }); - let is_even: bool = op.broadcast(Bob, is_even_at_bob); - if is_even { - let x_at_carol = op.comm(Bob, Carol, &self.x_at_bob); - op.locally(Carol, |un| { - let x = un.unwrap(&x_at_carol); - println!("x is even: {}", x); - }); - } - } -} -``` - -Notice that `BobCarolChoreography` only describes the behavior of Bob and Carol (see its location set `L`). `enclave` is an operator to execute a choreography only at locations that is included in the location set. In this case, if we invoke `BobCarolChoreography` with `enclave` in the main choreography, it will only be executed at Bob and Carol and not at Alice. - -```rust -{{#include ./header.txt}} -# fn get_random_number() -> u32 { -# 42 // for presentation purpose -# } -# struct BobCarolChoreography { -# x_at_bob: Located, -# }; -# impl Choreography for BobCarolChoreography { -# type L = LocationSet!(Bob, Carol); -# fn run(self, op: &impl ChoreoOp) { -# let is_even_at_bob: Located = op.locally(Bob, |un| { -# let x = un.unwrap(&self.x_at_bob); -# x % 2 == 0 -# }); -# let is_even: bool = op.broadcast(Bob, is_even_at_bob); -# if is_even { -# let x_at_carol = op.comm(Bob, Carol, &self.x_at_bob); -# op.locally(Carol, |un| { -# let x = un.unwrap(&x_at_carol); -# println!("x is even: {}", x); -# }); -# } -# } -# } -struct MainChoreography; -impl Choreography for MainChoreography { - type L = LocationSet!(Alice, Bob, Carol); - fn run(self, op: &impl ChoreoOp) { - let x_at_alice = op.locally(Alice, |_| { - get_random_number() - }); - let x_at_bob = op.comm(Alice, Bob, &x_at_alice); - op.enclave(BobCarolChoreography { - x_at_bob, - }); - } -} -``` - - diff --git a/chorus_book/src/guide-located-values.md b/chorus_book/src/guide-located-values.md index 648cbb4..ed6cdba 100644 --- a/chorus_book/src/guide-located-values.md +++ b/chorus_book/src/guide-located-values.md @@ -4,7 +4,7 @@ As we have seen in the [Choreography](./guide-choreography.md) section, a locate ## `MultiplyLocated` struct -The `MultiplyLocated` struct represents a located value that is available at multiple locations. It is a generic struct that takes two type parameters: a type parameter `V` that represents the type of the value, and a type parameter `L` that represents the location set where the value is available. +The `MultiplyLocated` struct represents a multiply located value (MLV) that is available at multiple locations. It is a generic struct that takes two type parameters: a type parameter `V` that represents the type of the value, and a type parameter `L` that represents the location set where the value is available. ```rust,ignore pub struct MultiplyLocated diff --git a/chorus_book/src/guide-locations.md b/chorus_book/src/guide-locations.md index 0d109b5..c70e037 100644 --- a/chorus_book/src/guide-locations.md +++ b/chorus_book/src/guide-locations.md @@ -59,5 +59,5 @@ Some operators, such as `multicast`, requires a value of `LocationSet` to be pas # struct Bob; # use chorus_lib::core::LocationSet; # -let alice_and_bob = ::new(); +let alice_and_bob = ::new(); ``` diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index f995e9d..a12db21 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -1,5 +1,5 @@ # extern crate chorus_lib; -# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, MultiplyLocated, Superposition, Runner, LocationSet}; +# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, MultiplyLocated, Superposition, Runner, LocationSet, Serialize, Deserialize}; # use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; # #[derive(ChoreographyLocation)] # struct Alice; From e1484c49ffa0d8ac01187d1b55084f782d342c89 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Sat, 30 Nov 2024 12:56:04 -0800 Subject: [PATCH 39/42] reorder items --- chorus_book/src/SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chorus_book/src/SUMMARY.md b/chorus_book/src/SUMMARY.md index 8aab5e3..a2b1f9e 100644 --- a/chorus_book/src/SUMMARY.md +++ b/chorus_book/src/SUMMARY.md @@ -10,8 +10,8 @@ - [Transport](./guide-transport.md) - [Projector](./guide-projector.md) - [Input and Output](./guide-input-and-output.md) + - [Runner](./guide-runner.md) - [Higher-order Choreography](./guide-higher-order-choreography.md) - [Location Polymorphism](./guide-location-polymorphism.md) - [Efficient Conditionals with Enclaves and MLVs](./guide-efficient-conditionals.md) - - [Runner](./guide-runner.md) - [Links](./links.md) From 5ac0739e1e955d93b7b1391f3977529bcfeb14f4 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Mon, 2 Dec 2024 15:08:37 -0800 Subject: [PATCH 40/42] remove `Superposition` --- chorus_derive/src/lib.rs | 67 +---------------------------------- chorus_lib/examples/runner.rs | 3 +- chorus_lib/src/core.rs | 37 +++++-------------- 3 files changed, 11 insertions(+), 96 deletions(-) diff --git a/chorus_derive/src/lib.rs b/chorus_derive/src/lib.rs index 53e8e12..e97f4cb 100644 --- a/chorus_derive/src/lib.rs +++ b/chorus_derive/src/lib.rs @@ -1,6 +1,6 @@ use proc_macro::{self, TokenStream}; use quote::quote; -use syn::{parse_macro_input, Data, DeriveInput, Fields}; +use syn::{parse_macro_input, DeriveInput}; #[proc_macro_derive(ChoreographyLocation)] pub fn derive_choreography_location(input: TokenStream) -> TokenStream { @@ -23,68 +23,3 @@ pub fn derive_choreography_location(input: TokenStream) -> TokenStream { }; output.into() } - -#[proc_macro_derive(Superposition)] -pub fn derive_superposition(input: TokenStream) -> TokenStream { - // Parse the input tokens into a syntax tree - let input = syn::parse_macro_input!(input as DeriveInput); - - // Get the name of the struct or enum - let name = &input.ident; - - // Generate the implementation of the Superposition trait - let expanded = match input.data { - Data::Struct(data) => match data.fields { - Fields::Named(fields) => { - let field_names = fields.named.iter().map(|field| &field.ident); - quote! { - impl Superposition for #name { - fn remote() -> Self { - #name { - #( #field_names: <_ as Superposition>::remote(), )* - } - } - } - } - } - Fields::Unnamed(fields) => { - let fields = (0..fields.unnamed.len()).map(|_| { - quote! { - <_ as Superposition>::remote() - } - }); - quote! { - impl Superposition for #name { - fn remote() -> Self { - #name( - #(#fields),* - ) - } - } - } - } - Fields::Unit => { - quote! { - impl Superposition for #name { - fn remote() -> Self { - #name - } - } - } - } - }, - Data::Enum(_) => { - quote! { - compile_error!("Superposition cannot be derived automatically for enums"); - } - } - Data::Union(_) => { - quote! { - compile_error!("Superposition cannot be derived automatically for unions"); - } - } - }; - - // Convert the generated tokens back into a TokenStream - TokenStream::from(expanded) -} diff --git a/chorus_lib/examples/runner.rs b/chorus_lib/examples/runner.rs index fbffbc9..b8adb18 100644 --- a/chorus_lib/examples/runner.rs +++ b/chorus_lib/examples/runner.rs @@ -1,6 +1,6 @@ extern crate chorus_lib; use chorus_lib::core::{ - ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, Runner, Superposition, + ChoreoOp, Choreography, ChoreographyLocation, Located, LocationSet, Runner, }; #[derive(ChoreographyLocation)] @@ -14,7 +14,6 @@ fn get_random_number() -> u32 { 42 // for presentation purpose } -#[derive(Superposition)] struct BobCarolResult { is_even_at_bob: Located, is_even_at_carol: Located, diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 60835ec..43b107c 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -34,20 +34,6 @@ pub trait ChoreographyLocation: Copy { pub trait Portable: Serialize + DeserializeOwned {} impl Portable for T {} -/// Represents a value that might *NOT* be located at a location. Values returned by `enclave` must satisfy this trait. -/// -/// In most cases, you don't need to implement this trait manually. You can derive it using `#[derive(Superposition)]` as long as all the fields consist of located values. -pub trait Superposition { - /// Constructs a struct that is *NOT* located at a location. - fn remote() -> Self; -} - -impl Superposition for () { - fn remote() -> Self { - () - } -} - /// Represents a value located at a location. pub type Located = MultiplyLocated; @@ -71,6 +57,14 @@ where phantom: PhantomData, } } + + /// Constructs a struct located at another location + fn remote() -> Self { + MultiplyLocated { + value: None, + phantom: PhantomData, + } + } } impl MultiplyLocated, LS2> @@ -104,19 +98,6 @@ where } } -impl Superposition for MultiplyLocated -where - L: LocationSet, -{ - /// Constructs a struct located at another location - fn remote() -> Self { - MultiplyLocated { - value: None, - phantom: PhantomData, - } - } -} - /// Represents a mapping from location names to values pub struct Quire where @@ -1495,4 +1476,4 @@ impl Runner { } extern crate chorus_derive; -pub use chorus_derive::{ChoreographyLocation, Superposition}; +pub use chorus_derive::ChoreographyLocation; From 497d18e004530f8b6390c421031467ec277a17d1 Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Mon, 2 Dec 2024 15:12:38 -0800 Subject: [PATCH 41/42] comments --- chorus_book/src/header.txt | 2 +- chorus_lib/src/core.rs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/chorus_book/src/header.txt b/chorus_book/src/header.txt index a12db21..1dbae7c 100644 --- a/chorus_book/src/header.txt +++ b/chorus_book/src/header.txt @@ -1,5 +1,5 @@ # extern crate chorus_lib; -# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, MultiplyLocated, Superposition, Runner, LocationSet, Serialize, Deserialize}; +# use chorus_lib::core::{ChoreoOp, Choreography, ChoreographyLocation, Projector, Located, MultiplyLocated, Runner, LocationSet, Serialize, Deserialize}; # use chorus_lib::transport::local::{LocalTransport, LocalTransportChannelBuilder}; # #[derive(ChoreographyLocation)] # struct Alice; diff --git a/chorus_lib/src/core.rs b/chorus_lib/src/core.rs index 43b107c..2530665 100644 --- a/chorus_lib/src/core.rs +++ b/chorus_lib/src/core.rs @@ -466,12 +466,12 @@ pub trait ChoreoOp { Sender: Member, D: Subset; - /// TODO: documentation + /// Obtains a normal value from a value located at all locations in the census fn naked(&self, data: MultiplyLocated) -> V where ChoreoLS: Subset; - /// TODO: documentation + /// Wraps a value into a located value at the current census fn unnaked(&self, data: V) -> MultiplyLocated; /// Calls a choreography. @@ -547,7 +547,9 @@ pub trait FanOutChoreography { type L: LocationSet; /// Locations looping over type QS: LocationSet; - /// TODO: documentation + /// The body of the choreography defined in terms of the operators provided by `ChoreoOp` + /// + /// `Q` is the location that the loop variable and is guaranteed to be a member of `L` and `QS`. fn run( &self, op: &impl ChoreoOp, @@ -566,7 +568,9 @@ pub trait FanInChoreography { type QS: LocationSet; /// Recipient locations type RS: LocationSet; - /// run a choreography + /// The body of the choreography defined in terms of the operators provided by `ChoreoOp` + /// + /// `Q` is the location that the loop variable and is guaranteed to be a member of `L` and `QS`. fn run( &self, op: &impl ChoreoOp, From 187e914619594cc9501aebdb38e394d1921b875a Mon Sep 17 00:00:00 2001 From: Shun Kashiwa Date: Mon, 2 Dec 2024 15:14:53 -0800 Subject: [PATCH 42/42] bump version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 154fe08..e0adc57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ members = ["chorus_lib", "chorus_derive"] resolver = "2" [workspace.package] -version = "0.3.0" +version = "0.4.0" edition = "2021" authors = ["Shun Kashiwa "] homepage = "https://lsd-ucsc.github.io/ChoRus/"