Skip to content

Commit

Permalink
feat: redistribution cap strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
vacekj committed Feb 5, 2024
1 parent fdde5e8 commit c06483a
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 11 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Overview

The `matcha_funding` library is a Rust crate providing functionality to calculate quadratic funding distributions based
The `pluralistic-rs` library is a Rust crate providing functionality to calculate quadratic funding distributions based
on a set of contributions. The library includes a robust implementation for generating random contributions, managing
contributions, and applying quadratic funding formulas to distribute a matching pot of funds.

Expand All @@ -16,11 +16,11 @@ contributions, and applying quadratic funding formulas to distribute a matching

## Installation

Add `matcha_funding` to your `Cargo.toml` dependencies:
Add `pluralistic-rs` to your `Cargo.toml` dependencies:

```toml
[dependencies]
matcha_funding = "0.1.0"
pluralistic-rs = "0.1.0"
```

## Usage
Expand All @@ -39,7 +39,7 @@ matcha_funding = "0.1.0"
## Example

```rust
use matcha_funding::{Contribution, calculate_linear_qf, LinearQfOptions, Random};
use pluralistic_rs::{Contribution, calculate_linear_qf, LinearQfOptions, Random};

fn main() {
// Generate random contributions
Expand Down
89 changes: 82 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,36 @@ pub fn calculate_linear_qf(
}
}

// Apply matching cap and redistribute if necessary
if let Some(cap) = options.matching_cap_amount {
match options.matching_cap_strategy {
MatchingCapStrategy::Cap => {
for matcha in &mut distributions {
matcha.matcha = matcha.matcha.clamp(0.0, cap);
}
let mut overflow_total = 0f64;
let mut eligible_for_redistribution = 0usize;

// First pass to apply cap and calculate overflow
for matcha in &mut distributions {
if matcha.matcha > cap {
overflow_total += matcha.matcha - cap;
matcha.matcha = cap;
} else {
eligible_for_redistribution += 1;
}
MatchingCapStrategy::Redistribute => {
todo!("redistribution strategy is not implemented (and unfair)")
}

// Redistribution logic
if matches!(
options.matching_cap_strategy,
MatchingCapStrategy::Redistribute
) && overflow_total > 0f64
{
let redistribution_amount = overflow_total / eligible_for_redistribution as f64;
for matcha in &mut distributions {
if matcha.matcha < cap {
matcha.matcha += redistribution_amount;
// Ensure not to exceed the cap after redistribution
if matcha.matcha > cap {
matcha.matcha = cap;
}
}
}
}
}
Expand Down Expand Up @@ -186,4 +207,58 @@ mod tests {
.collect::<Vec<Contribution>>();
calculate_linear_qf(contributions, 10_000f64, LinearQfOptions::default());
}

#[test]
fn test_redistribution_strategy() {
let mut rng = rand::thread_rng();

let a_contribs = arr![Contribution {
recipient: "A".into(),
amount: 200f64,
sender: rng.gen::<char>().into(),
}; 5]
.to_vec();
let b_contribs = arr![Contribution {
recipient: "B".into(),
amount: 500f64,
sender: rng.gen::<char>().into(),
}; 2]
.to_vec();
let c_contribs = arr![Contribution {
recipient: "C".into(),
amount: 50f64,
sender: rng.gen::<char>().into(),
}; 20]
.to_vec();
let contributions = vec![a_contribs, b_contribs, c_contribs]
.into_iter()
.flatten()
.collect::<Vec<Contribution>>();

let matching_pot = 10000.0;
let cap = 10.0; // Set a cap that would force redistribution

let options = LinearQfOptions {
matching_cap_amount: Some(cap),
matching_cap_strategy: MatchingCapStrategy::Redistribute,
upscale: false, // Upscaling not relevant for this test
};

let distributions = calculate_linear_qf(contributions, matching_pot, options);

// Verify that none of the distributions exceed the cap
assert!(
distributions.iter().all(|d| d.matcha <= cap),
"All distributions must be within the cap."
);

// Calculate total distributed amount to ensure it's within the matching pot
let total_distributed: f64 = distributions.iter().map(|d| d.matcha).sum();

// The total distributed amount should be less than or equal to the matching pot
assert!(
total_distributed <= matching_pot,
"Total distributed amount must not exceed the matching pot."
);
}
}

0 comments on commit c06483a

Please sign in to comment.