mirror of
https://github.com/mii443/practiral-irp.git
synced 2025-08-22 07:05:33 +00:00
first commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
154
Cargo.lock
generated
Normal file
154
Cargo.lock
generated
Normal file
@ -0,0 +1,154 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "practical_irp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rand",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "practical_irp"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.8"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
64
src/client.rs
Normal file
64
src/client.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use crate::{Grid, IrpParams, Matrix, Observation};
|
||||
use rand::Rng;
|
||||
|
||||
pub struct Client {
|
||||
matrix: Matrix,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new(matrix: Matrix) -> Self {
|
||||
Self { matrix }
|
||||
}
|
||||
|
||||
pub fn generate_observation(&self, grid: Grid) -> Observation {
|
||||
let mut rng = rand::thread_rng();
|
||||
let max_i = self.matrix.params.matrix_rows();
|
||||
let i = rng.gen_range(0..max_i) as u32;
|
||||
|
||||
let v = self
|
||||
.matrix
|
||||
.get(i as usize, grid.0)
|
||||
.expect("Invalid grid or index");
|
||||
|
||||
Observation { v, i }
|
||||
}
|
||||
|
||||
pub fn params(&self) -> &IrpParams {
|
||||
&self.matrix.params
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_observation_generation() {
|
||||
let params = IrpParams::new(16, 4, 10).unwrap();
|
||||
let matrix = Matrix::generate_random(params.clone());
|
||||
let client = Client::new(matrix.clone());
|
||||
|
||||
let grid = Grid::new(5);
|
||||
let obs = client.generate_observation(grid);
|
||||
|
||||
assert!(obs.i < params.matrix_rows() as u32);
|
||||
assert_eq!(obs.v, matrix.get(obs.i as usize, grid.0).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_observations() {
|
||||
let params = IrpParams::new(16, 4, 10).unwrap();
|
||||
let matrix = Matrix::generate_random(params);
|
||||
let client = Client::new(matrix);
|
||||
|
||||
let grid = Grid::new(3);
|
||||
let observations: Vec<_> = (0..100)
|
||||
.map(|_| client.generate_observation(grid))
|
||||
.collect();
|
||||
|
||||
for obs in &observations {
|
||||
assert!(obs.i < 16);
|
||||
assert!(obs.v < client.params().modulus());
|
||||
}
|
||||
}
|
||||
}
|
133
src/irp.rs
Normal file
133
src/irp.rs
Normal file
@ -0,0 +1,133 @@
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct IrpParams {
|
||||
pub b: u32, // 送信ビット数
|
||||
pub m: u32, // m < B
|
||||
pub d: u32, // グリッド数
|
||||
}
|
||||
|
||||
impl IrpParams {
|
||||
pub fn new(b: u32, m: u32, d: u32) -> Result<Self, String> {
|
||||
if m >= b {
|
||||
return Err("m must be less than B".to_string());
|
||||
}
|
||||
if b > 64 {
|
||||
return Err("B must be less than or equal to 64".to_string());
|
||||
}
|
||||
Ok(Self { b, m, d })
|
||||
}
|
||||
|
||||
pub fn modulus(&self) -> u64 {
|
||||
1u64 << (self.b - self.m)
|
||||
}
|
||||
|
||||
pub fn matrix_rows(&self) -> usize {
|
||||
1usize << self.m
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Matrix {
|
||||
pub data: Vec<Vec<u64>>,
|
||||
pub params: IrpParams,
|
||||
}
|
||||
|
||||
impl Matrix {
|
||||
pub fn generate_random(params: IrpParams) -> Self {
|
||||
let mut rng = rand::thread_rng();
|
||||
let rows = params.matrix_rows();
|
||||
let cols = params.d as usize;
|
||||
let modulus = params.modulus();
|
||||
|
||||
let data = (0..rows)
|
||||
.map(|_| {
|
||||
(0..cols)
|
||||
.map(|_| rng.gen_range(0..modulus))
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self { data, params }
|
||||
}
|
||||
|
||||
pub fn get(&self, i: usize, g: u32) -> Option<u64> {
|
||||
if i >= self.data.len() || g >= self.params.d {
|
||||
return None;
|
||||
}
|
||||
Some(self.data[i][g as usize])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Observation {
|
||||
pub v: u64,
|
||||
pub i: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Grid(pub u32);
|
||||
|
||||
impl Grid {
|
||||
pub fn new(g: u32) -> Self {
|
||||
Self(g)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn estimate_possible_grids(matrix: &Matrix, observation: &Observation) -> HashSet<Grid> {
|
||||
let mut possible_grids = HashSet::new();
|
||||
|
||||
for g in 0..matrix.params.d {
|
||||
if let Some(v) = matrix.get(observation.i as usize, g) {
|
||||
if v == observation.v {
|
||||
possible_grids.insert(Grid::new(g));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
possible_grids
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_irp_params() {
|
||||
assert!(IrpParams::new(32, 8, 1000).is_ok());
|
||||
assert!(IrpParams::new(32, 32, 1000).is_err());
|
||||
assert!(IrpParams::new(65, 8, 1000).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_matrix_generation() {
|
||||
let params = IrpParams::new(32, 8, 100).unwrap();
|
||||
let matrix = Matrix::generate_random(params.clone());
|
||||
|
||||
assert_eq!(matrix.data.len(), 256); // 2^8
|
||||
assert_eq!(matrix.data[0].len(), 100);
|
||||
|
||||
for row in &matrix.data {
|
||||
for &val in row {
|
||||
assert!(val < params.modulus());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_grid_estimation() {
|
||||
let params = IrpParams::new(16, 4, 10).unwrap();
|
||||
let mut matrix = Matrix::generate_random(params);
|
||||
|
||||
matrix.data[5][3] = 42;
|
||||
matrix.data[5][7] = 42;
|
||||
|
||||
let obs = Observation { v: 42, i: 5 };
|
||||
let possible = estimate_possible_grids(&matrix, &obs);
|
||||
|
||||
assert!(possible.contains(&Grid::new(3)));
|
||||
assert!(possible.contains(&Grid::new(7)));
|
||||
}
|
||||
}
|
7
src/lib.rs
Normal file
7
src/lib.rs
Normal file
@ -0,0 +1,7 @@
|
||||
pub mod irp;
|
||||
pub mod client;
|
||||
pub mod server;
|
||||
|
||||
pub use irp::*;
|
||||
pub use client::Client;
|
||||
pub use server::Server;
|
68
src/main.rs
Normal file
68
src/main.rs
Normal file
@ -0,0 +1,68 @@
|
||||
use practical_irp::{Client, Grid, IrpParams, Matrix, Server};
|
||||
|
||||
fn index_pair_to_grid(a: u32, b: u32, size: u32) -> Grid {
|
||||
Grid::new(a * size + b)
|
||||
}
|
||||
|
||||
fn grid_to_index_pair(grid: &Grid, size: u32) -> (u32, u32) {
|
||||
let index = grid.0;
|
||||
(index / size, index % size)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let size = 31u32;
|
||||
let params = IrpParams::new(16, 8, size.pow(2).pow(2)).expect("Invalid parameters");
|
||||
println!("IRP Parameters:");
|
||||
println!(" B (transmission bits): {}", params.b);
|
||||
println!(" m: {}", params.m);
|
||||
println!(" d (number of grids): {}", params.d);
|
||||
println!(" Modulus: {}", params.modulus());
|
||||
println!(" Matrix rows: {}", params.matrix_rows());
|
||||
println!();
|
||||
|
||||
let matrix = Matrix::generate_random(params);
|
||||
|
||||
let mut server = Server::new(matrix.clone());
|
||||
let client = Client::new(matrix);
|
||||
|
||||
println!("Simulating client observations...");
|
||||
let true_grids = vec![
|
||||
index_pair_to_grid(7, 7, size),
|
||||
index_pair_to_grid(8, 8, size),
|
||||
index_pair_to_grid(15, 20, size),
|
||||
];
|
||||
|
||||
for &grid in &true_grids {
|
||||
for _ in 0..255 {
|
||||
let observation = client.generate_observation(grid);
|
||||
server.process_observation(&observation);
|
||||
}
|
||||
println!(
|
||||
" Generated 10 observations for grid {:?}",
|
||||
grid_to_index_pair(&grid, size)
|
||||
);
|
||||
}
|
||||
|
||||
println!("\nTop 5 estimated grids:");
|
||||
let top_grids = server.get_top_grids(5);
|
||||
for (i, (grid, score)) in top_grids.iter().enumerate() {
|
||||
println!(
|
||||
" {}. Grid {:?} - score: {:.4}",
|
||||
i + 1,
|
||||
grid_to_index_pair(grid, size),
|
||||
score
|
||||
);
|
||||
}
|
||||
|
||||
println!("\nNormalized heatmap for top grids:");
|
||||
let normalized = server.get_normalized_heatmap();
|
||||
for (grid, _count) in &top_grids {
|
||||
if let Some(&prob) = normalized.get(grid) {
|
||||
println!(
|
||||
" Grid {:?} - probability: {:.4}",
|
||||
grid_to_index_pair(grid, size),
|
||||
prob
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
131
src/server.rs
Normal file
131
src/server.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use crate::{estimate_possible_grids, Grid, Matrix, Observation};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Server {
|
||||
matrix: Matrix,
|
||||
heatmap: HashMap<Grid, f64>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn new(matrix: Matrix) -> Self {
|
||||
Self {
|
||||
matrix,
|
||||
heatmap: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_observation(&mut self, observation: &Observation) {
|
||||
let possible_grids = estimate_possible_grids(&self.matrix, observation);
|
||||
let num_candidates = possible_grids.len() as f64;
|
||||
|
||||
if num_candidates > 0.0 {
|
||||
let increment = 1.0;
|
||||
for grid in possible_grids {
|
||||
*self.heatmap.entry(grid).or_insert(0.0) += increment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_observations(&mut self, observations: &[Observation]) {
|
||||
for obs in observations {
|
||||
self.process_observation(obs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_heatmap(&self) -> &HashMap<Grid, f64> {
|
||||
&self.heatmap
|
||||
}
|
||||
|
||||
pub fn get_normalized_heatmap(&self) -> HashMap<Grid, f64> {
|
||||
let total: f64 = self.heatmap.values().sum();
|
||||
if total == 0.0 {
|
||||
return HashMap::new();
|
||||
}
|
||||
|
||||
self.heatmap
|
||||
.iter()
|
||||
.map(|(&grid, &score)| (grid, score / total))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_top_grids(&self, n: usize) -> Vec<(Grid, f64)> {
|
||||
let mut grids: Vec<_> = self
|
||||
.heatmap
|
||||
.iter()
|
||||
.map(|(&grid, &score)| (grid, score))
|
||||
.collect();
|
||||
|
||||
grids.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
|
||||
grids.truncate(n);
|
||||
grids
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::IrpParams;
|
||||
|
||||
#[test]
|
||||
fn test_server_aggregation() {
|
||||
let params = IrpParams::new(16, 4, 10).unwrap();
|
||||
let mut matrix = Matrix::generate_random(params);
|
||||
|
||||
matrix.data[2][5] = 100;
|
||||
matrix.data[7][5] = 100;
|
||||
matrix.data[2][8] = 100;
|
||||
|
||||
let mut server = Server::new(matrix);
|
||||
|
||||
server.process_observation(&Observation { v: 100, i: 2 });
|
||||
server.process_observation(&Observation { v: 100, i: 7 });
|
||||
|
||||
let heatmap = server.get_heatmap();
|
||||
assert_eq!(heatmap[&Grid::new(5)], 1.0); // 2回観測、各回1/2を加算
|
||||
assert_eq!(heatmap[&Grid::new(8)], 0.5); // 1回観測、1/2を加算
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalized_heatmap() {
|
||||
let params = IrpParams::new(16, 4, 10).unwrap();
|
||||
let mut matrix = Matrix::generate_random(params);
|
||||
|
||||
// 固定の値を設定して予測可能にする
|
||||
matrix.data[0][5] = 10;
|
||||
matrix.data[1][7] = 20;
|
||||
matrix.data[2][3] = 30;
|
||||
|
||||
let mut server = Server::new(matrix);
|
||||
|
||||
let observations = vec![
|
||||
Observation { v: 10, i: 0 },
|
||||
Observation { v: 20, i: 1 },
|
||||
Observation { v: 30, i: 2 },
|
||||
];
|
||||
|
||||
server.process_observations(&observations);
|
||||
|
||||
let normalized = server.get_normalized_heatmap();
|
||||
if !normalized.is_empty() {
|
||||
let total: f64 = normalized.values().sum();
|
||||
assert!((total - 1.0).abs() < 1e-10);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_top_grids() {
|
||||
let params = IrpParams::new(16, 4, 10).unwrap();
|
||||
let matrix = Matrix::generate_random(params);
|
||||
let mut server = Server::new(matrix);
|
||||
|
||||
server.heatmap.insert(Grid::new(1), 10.0);
|
||||
server.heatmap.insert(Grid::new(2), 20.0);
|
||||
server.heatmap.insert(Grid::new(3), 5.0);
|
||||
server.heatmap.insert(Grid::new(4), 15.0);
|
||||
|
||||
let top = server.get_top_grids(2);
|
||||
assert_eq!(top.len(), 2);
|
||||
assert_eq!(top[0], (Grid::new(2), 20.0));
|
||||
assert_eq!(top[1], (Grid::new(4), 15.0));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user