Skip to content

Commit

Permalink
Implement order-n parametrizable model & binary (#13)
Browse files Browse the repository at this point in the history
I'll add & analyze results later
  • Loading branch information
mitiko authored Mar 4, 2024
2 parents 380bb6e + 3112202 commit b1a8874
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 5 deletions.
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,9 @@ name = "order0"

[[bin]]
name = "ac-over-huffman"

[[bin]]
name = "cmp"

[[bin]]
name = "ordern"
6 changes: 4 additions & 2 deletions src/bin/ac-over-huffman/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@ fn exec(buf: &[u8], huffman_size: u8) -> Result<u64> {
}
ac.flush(&mut writer)?;

let time = timer.elapsed();
println!(
"[ac-over-huffman] [hsize: {:02} b{:02}] csize: {} (ratio: {:.3}), ctime: {:?}",
"[ac-over-huff] [hsize: {:02} b{:02}] csize: {} (ratio: {:.3}), ctime: {:?} ({:?} per bit)",
huffman_size,
8, // bits in context
writer.result(),
writer.result() as f64 / buf.len() as f64,
timer.elapsed()
time,
time.div_f64(buf.len() as f64 * 8.0)
);

Ok(writer.result())
Expand Down
54 changes: 54 additions & 0 deletions src/bin/cmp/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use std::{
fs::File,
io::{BufWriter, Result},
time::Instant,
};

use weath3rb0i::{
entropy_coding::{arithmetic_coder::ArithmeticCoder, io::ACWriter},
helpers::cmp,
models::{Model, Order0, OrderN},
unroll_for,
};

fn main() -> Result<()> {
let model = Order0::new();
exec(model, "order0")?;

let model = OrderN::new(11, 3);
exec(model, "ordern")?;

cmp("order0.bin", "ordern.bin")?;

Ok(())
}

fn exec(mut model: impl Model, name: &str) -> Result<()> {
let timer = Instant::now();
let buf = std::fs::read("/Users/mitiko/_data/enwik7")?;
let mut ac = ArithmeticCoder::new_coder();
let file = File::create(format!("{}.bin", name))?;
let mut writer = ACWriter::new(BufWriter::new(&file));

for byte in &buf {
unroll_for!(bit in byte, {
let p = model.predict();
model.update(bit);
ac.encode(bit, p, &mut writer)?;
});
}
ac.flush(&mut writer)?;

let res = File::metadata(&file)?.len();
let time = timer.elapsed();
println!(
"[{}] csize: {} (ratio: {:.3}), ctime: {:?} ({:?} per bit)",
name,
res,
res as f64 / buf.len() as f64,
time,
time.div_f64(buf.len() as f64 * 8.0)
);

Ok(())
}
6 changes: 4 additions & 2 deletions src/bin/order0/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ fn main() -> Result<()> {
}
ac.flush(&mut writer)?;

let time = timer.elapsed();
println!(
"[order0] csize: {} (ratio: {:.3}), ctime: {:?}",
"[order0] csize: {} (ratio: {:.3}), ctime: {:?} ({:?} per bit)",
writer.result(),
writer.result() as f64 / buf.len() as f64,
timer.elapsed()
time,
time.div_f64(buf.len() as f64 * 8.0)
);

Ok(())
Expand Down
69 changes: 69 additions & 0 deletions src/bin/ordern/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use std::{io::Result, time::Instant};
use weath3rb0i::{
entropy_coding::arithmetic_coder::ArithmeticCoder,
helpers::ACStats,
models::{Model, OrderN},
u64, unroll_for,
};

fn main() -> Result<()> {
let buf = std::fs::read("/Users/mitiko/_data/book1")?;

let levels = 2;
let mut best = vec![u64!(buf.len()); levels];
let mut params = vec![(0, 0); levels];

for ctx_bits in 8..=26 {
best[1] = u64!(buf.len());
params[1] = (0, 0);
for alignment_bits in 0..=4 {
let res = exec(&buf, ctx_bits, alignment_bits)?;
for i in 0..levels {
if res > best[i] {
continue;
}
best[i] = res;
params[i] = (ctx_bits, alignment_bits);
}
}
println!(
"-> best: {} for [ctx: {}, align: {}]",
best[1], params[1].0, params[1].1
);
}
println!(
"-> gloabl best: {} for [ctx: {}, align: {}]",
best[0], params[0].0, params[0].1
);

Ok(())
}

fn exec(buf: &[u8], ctx_bits: u8, alignment_bits: u8) -> Result<u64> {
let timer = Instant::now();
let mut ac = ArithmeticCoder::new_coder();
let mut model = OrderN::new(ctx_bits, alignment_bits);
let mut writer = ACStats::new();

for &byte in buf {
unroll_for!(bit in byte, {
let p = model.predict();
model.update(bit);
ac.encode(bit, p, &mut writer)?;
});
}
ac.flush(&mut writer)?;

let time = timer.elapsed();
println!(
"[ordern] [ctx: {:2} align: {}] csize: {} (ratio: {:.3}), ctime: {:?} ({:?} per bit)",
ctx_bits,
alignment_bits,
writer.result(),
writer.result() as f64 / buf.len() as f64,
time,
time.div_f64(buf.len() as f64 * 8.0)
);

Ok(writer.result())
}
3 changes: 2 additions & 1 deletion src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ pub mod counter;
pub mod order0;
pub mod order0entropy;
pub mod order1;
pub mod ordern;
pub mod stationary;

pub use self::{counter::*, order0::*, order0entropy::*, order1::*};
pub use self::{counter::*, order0::*, order0entropy::*, order1::*, ordern::*};
pub use crate::state_table::*;

pub trait Model {
Expand Down
42 changes: 42 additions & 0 deletions src/models/ordern.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use super::{counter::Counter, Model};
use crate::usize;

pub struct OrderN {
stats: Vec<Counter>,
ctx: u32,
history: u32,
alignment: u8,
bits_in_context: u8,
alignment_bits: u8,
}

impl OrderN {
pub fn new(bits_in_context: u8, alignment_bits: u8) -> Self {
Self {
stats: vec![Counter::new(); 1 << bits_in_context],
ctx: 0,
history: 0,
alignment: 0,
bits_in_context,
alignment_bits,
}
}
}

impl Model for OrderN {
fn predict(&self) -> u16 {
self.stats[usize!(self.ctx)].p()
}

fn update(&mut self, bit: u8) {
self.stats[usize!(self.ctx)].update(bit);

let mask_bits = self.bits_in_context - self.alignment_bits;
let mask = (1 << mask_bits) - 1;
let alignment_mask = (1 << self.alignment_bits) - 1;

self.history = ((self.history << 1) | u32::from(bit)) & mask;
self.alignment = (self.alignment + 1) & alignment_mask;
self.ctx = (self.history << self.alignment_bits) | u32::from(self.alignment)
}
}

0 comments on commit b1a8874

Please sign in to comment.