Skip to content

Commit a366c59

Browse files
save
1 parent e46b58d commit a366c59

4 files changed

Lines changed: 126 additions & 11 deletions

File tree

.envrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
use flake

flake.nix

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
description = "A flake providing a Python development shell with uv";
3+
4+
inputs = {
5+
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
6+
flake-utils.url = "github:numtide/flake-utils";
7+
};
8+
9+
outputs = { self, nixpkgs, flake-utils, ... }:
10+
flake-utils.lib.eachDefaultSystem (system:
11+
let
12+
pkgs = import nixpkgs { inherit system; };
13+
in {
14+
devShells.default = pkgs.mkShell {
15+
buildInputs = [
16+
pkgs.python312
17+
pkgs.uv
18+
];
19+
shellHook = ''
20+
echo "Python: $(python --version)"
21+
echo "uv: $(uv --version)"
22+
'';
23+
};
24+
}
25+
);
26+
}

src/lib.rs

Lines changed: 96 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,66 @@
11
use pyo3::prelude::*;
22
use std::collections::{HashMap, HashSet};
3+
use std::hash::Hash;
4+
use std::collections::hash_map::Entry;
35

46
/// Formats the sum of two numbers as string.
57
#[pyfunction]
68
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
79
Ok((a + b).to_string())
810
}
911

12+
fn increment_and_remove_if_zero<T: std::cmp::Eq + Hash, N: std::ops::AddAssign + std::cmp::PartialEq<usize>>(map: &mut HashMap<T, N>, key: T, amount: N) -> () {
13+
match map.entry(key) {
14+
Entry::Occupied(mut entry) => {
15+
// add
16+
*entry.get_mut() += amount;
17+
18+
// if the value becomes 0 as a result remove.
19+
if *entry.get() == 0 {
20+
entry.remove();
21+
}
22+
}
23+
Entry::Vacant(entry) => {
24+
if amount != 0 {
25+
entry.insert(amount);
26+
}
27+
}
28+
}
29+
}
30+
31+
fn decrement_or_remove<T: std::cmp::Eq + Hash>(map: &mut HashMap<T, usize>, key: T, amount: usize) -> () {
32+
match map.entry(key) {
33+
Entry::Occupied(mut entry) => {
34+
// add
35+
*entry.get_mut() -= amount;
36+
37+
// if the value becomes 0 as a result remove.
38+
if *entry.get() == 0 {
39+
entry.remove();
40+
}
41+
}
42+
Entry::Vacant(_) => {}
43+
}
44+
}
45+
1046
// borrows:
1147
// tok_to_count: hashmap
1248
fn get_pairs(
1349
tok_to_count: &HashMap<Vec<Vec<u8>>, usize>,
1450
) -> (
1551
HashMap<(Vec<u8>, Vec<u8>), usize>,
16-
HashMap<(Vec<u8>, Vec<u8>), HashSet<Vec<u8>>>,
52+
HashMap<(Vec<u8>, Vec<u8>), HashSet<Vec<Vec<u8>>>>,
1753
) {
1854
let mut pair_to_count: HashMap<(Vec<u8>, Vec<u8>), usize> = HashMap::new();
19-
let mut pair_to_toks: HashMap<(Vec<u8>, Vec<u8>), HashSet<Vec<u8>>> = HashMap::new();
55+
let mut pair_to_toks: HashMap<(Vec<u8>, Vec<u8>), HashSet<Vec<Vec<u8>>>> = HashMap::new();
2056

2157
for (tok, count) in tok_to_count {
2258
// not big enough for a pair.
2359
if tok.len() < 2 {
2460
continue;
2561
}
2662
for i in 0..(tok.len() - 1) {
27-
let pair = (tok[i], tok[i+1]);
63+
let pair = (tok[i].clone(), tok[i+1].clone());
2864

2965
match pair_to_count.entry(pair.clone()) {
3066
std::collections::hash_map::Entry::Occupied(mut e) => {
@@ -38,7 +74,7 @@ fn get_pairs(
3874
pair_to_toks
3975
.entry(pair.clone())
4076
.or_insert(HashSet::new())
41-
.insert(tok);
77+
.insert(tok.clone());
4278
}
4379
}
4480

@@ -48,10 +84,10 @@ fn get_pairs(
4884
/// we take a map from vector of bytes and a max vocab size.
4985
/// and we return?
5086
#[pyfunction]
51-
fn merge(tok_to_count: HashMap<Vec<Vec<u8>>, usize>, max: usize) -> PyResult<Vec<(Vec<u8>, Vec<u8>)>> {
87+
fn merge(mut tok_to_count: HashMap<Vec<Vec<u8>>, usize>, max: usize) -> PyResult<Vec<(Vec<u8>, Vec<u8>)>> {
5288
let mut max_pairs = vec![(vec![0; 0], vec![0; 0]); max];
5389
let (mut pair_to_count, mut pair_to_toks) = get_pairs(&tok_to_count);
54-
let max_heap =
90+
// TODO: maintain heap of max-pairs, instead of finding the max_pair on every loop.
5591

5692
while max_pairs.len() < max {
5793
let max_pair_opt = max_pairs.iter()
@@ -63,13 +99,14 @@ fn merge(tok_to_count: HashMap<Vec<Vec<u8>>, usize>, max: usize) -> PyResult<Vec
6399
max_pairs.push(max_pair.clone());
64100
// ---------------- EFFICIENTLY UPDATE ----------------
65101
let max_pair_to_toks = &pair_to_toks[&max_pair];
102+
66103
// for every tok that contains max_pair
67104
for tok in max_pair_to_toks.clone() {
68105
let tok_count = tok_to_count[&tok.clone()];
69106

70-
// for every pair
107+
// for every pair in tok
71108
for i in 0..tok.len() - 1 {
72-
let pair = (vec![tok[i]], vec![tok[i+1]]);
109+
let pair = (tok[i].clone(), tok[i+1].clone());
73110

74111
match pair_to_toks.entry(pair.clone()) {
75112
std::collections::hash_map::Entry::Occupied(mut e) => {
@@ -96,14 +133,62 @@ fn merge(tok_to_count: HashMap<Vec<Vec<u8>>, usize>, max: usize) -> PyResult<Vec
96133
std::collections::hash_map::Entry::Vacant(_) => {}
97134
}
98135
}
99-
let new_tok: Vec<u8>= Vec::new();
136+
137+
// construct new_tok
138+
let mut new_tok: Vec<Vec<u8>>= Vec::new();
139+
100140
let mut i = 0;
141+
101142
while i < tok.len() {
102-
if i < tok.len() - 1 && (tok[i], tok[i+1]) == max_pair {
103-
new_tok.append(tok[i] + tok[i+1]);
143+
if i < tok.len() - 1 && (tok[i].clone(), tok[i+1].clone()) == max_pair {
144+
let new_vocab = [&tok[i][..], &tok[i+1][..]].concat();
145+
new_tok.push(new_vocab);
146+
i += 2;
147+
} else {
148+
new_tok.push(tok[i].clone());
149+
i += 1
150+
}
151+
}
152+
153+
// increment new_tok count by tok_count
154+
*tok_to_count.entry(new_tok.clone()).or_insert(0) += tok_count;
155+
156+
// decrement tok count by tok_count
157+
// if tok_count is zero -> remove tok entry all together.
158+
decrement_or_remove(&mut tok_to_count, tok.clone(), tok_count);
159+
160+
// for every pair in new_tok
161+
for i in 0..new_tok.clone().len() - 1 {
162+
let pair = (tok[i].clone(), tok[i+1].clone());
163+
164+
match pair_to_toks.entry(pair.clone()) {
165+
std::collections::hash_map::Entry::Occupied(mut e) => {
166+
let set = e.get_mut();
167+
168+
// remove from pair's toks.
169+
set.insert(new_tok.clone());
170+
}
171+
std::collections::hash_map::Entry::Vacant(_) => {}
172+
}
173+
174+
match pair_to_count.entry(pair.clone()) {
175+
std::collections::hash_map::Entry::Occupied(mut e) => {
176+
*e.get_mut() -= tok_count; // remove tok_count from pair count.
177+
178+
// if pair's count is zero.
179+
if *e.get() == 0 {
180+
// remove pair's pair_to_count entry
181+
e.remove();
182+
183+
// TODO: also remove the pair's pair_to_toks entry
184+
}
185+
}
186+
std::collections::hash_map::Entry::Vacant(_) => {}
104187
}
105188
}
106189
}
190+
191+
107192

108193
// ---------------- EFFICIENTLY UPDATE ----------------
109194
}

test.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from rusty_tokey import sum_as_string
2+
3+
print(sum_as_string(2,2))

0 commit comments

Comments
 (0)