@@ -11,9 +11,29 @@ use rayon::prelude::*;
1111
1212const PAT : & str = r"'(?:[sdmt]|ll|ve|re)| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+" ;
1313
14- // (NVM) TODO: maybe start accepting &[u8] instead? keep in mind all hell might break loose.
14+ struct TokenInterner {
15+ tokens : Vec < Vec < u8 > > ,
16+ token_to_id : HashMap < Vec < u8 > , u32 >
17+ }
18+
19+ type TokenId = u32 ;
20+
21+ impl TokenInterner {
22+ fn intern ( & mut self , token : Vec < u8 > ) -> TokenId {
23+ if let Some ( & id) = self . token_to_id . get ( & token) {
24+ id
25+ } else {
26+ let id = self . tokens . len ( ) as TokenId ;
27+ self . token_to_id . insert ( token. clone ( ) , id) ;
28+ self . tokens . push ( token) ;
29+ id
30+ }
31+ }
1532
16- // TODO: actually use Cow<u8> instead of Vec<u8>
33+ fn get_bytes ( & self , id : TokenId ) -> & [ u8 ] {
34+ & self . tokens [ id as usize ]
35+ }
36+ }
1737
1838static RE : Lazy < Regex > = Lazy :: new ( || Regex :: new ( PAT ) . unwrap ( ) ) ;
1939
@@ -50,14 +70,16 @@ fn decrement_or_remove<T: std::cmp::Eq + Hash>(map: &mut HashMap<T, usize>, key:
5070// borrows:
5171// tok_to_count: hashmap
5272fn get_pairs (
53- tok_to_count : & HashMap < Vec < Vec < u8 > > , usize > ,
73+ tok_to_count : & HashMap < Vec < TokenId > , usize > ,
74+ interner : & mut TokenInterner
5475) -> (
55- HashMap < ( Vec < u8 > , Vec < u8 > ) , usize > ,
56- HashMap < ( Vec < u8 > , Vec < u8 > ) , HashSet < Vec < Vec < u8 > > > > ,
76+ HashMap < ( TokenId , TokenId ) , usize > ,
77+ HashMap < ( TokenId , TokenId ) , HashSet < Vec < TokenId > > > ,
5778 BinaryHeap < PairHeapEntry >
5879) {
59- let mut pair_to_count: HashMap < ( Vec < u8 > , Vec < u8 > ) , usize > = HashMap :: new ( ) ;
60- let mut pair_to_toks: HashMap < ( Vec < u8 > , Vec < u8 > ) , HashSet < Vec < Vec < u8 > > > > = HashMap :: new ( ) ;
80+ let estimated_pairs = tok_to_count. len ( ) * 2 ;
81+ let mut pair_to_count: HashMap < ( TokenId , TokenId ) , usize > = HashMap :: with_capacity ( estimated_pairs) ;
82+ let mut pair_to_toks: HashMap < ( TokenId , TokenId ) , HashSet < Vec < TokenId > > > = HashMap :: with_capacity ( estimated_pairs) ;
6183 let mut heap = BinaryHeap :: new ( ) ;
6284
6385 for ( tok, count) in tok_to_count {
@@ -68,24 +90,19 @@ fn get_pairs(
6890 for i in 0 ..( tok. len ( ) - 1 ) {
6991 let pair = ( tok[ i] . clone ( ) , tok[ i+1 ] . clone ( ) ) ;
7092
71- let new_count = match pair_to_count. entry ( pair. clone ( ) ) {
72- std:: collections:: hash_map:: Entry :: Occupied ( mut e) => {
73- * e. get_mut ( ) += count; // Increment by counter if exists
74- * e. get ( )
75- }
76- std:: collections:: hash_map:: Entry :: Vacant ( e) => {
77- e. insert ( * count) ; // Insert counter if doesn't exist
78- * count
79- }
93+ let new_count = {
94+ let entry = pair_to_count. entry ( pair. clone ( ) ) . or_insert ( 0 ) ;
95+ * entry += count;
96+ * entry
8097 } ;
8198
8299 heap. push ( PairHeapEntry {
83100 count : new_count,
84- pair : pair. clone ( ) ,
101+ pair : ( interner . get_bytes ( pair. 0 ) . to_vec ( ) , interner . get_bytes ( pair . 1 ) . to_vec ( ) ) ,
85102 } ) ;
86103
87104 pair_to_toks
88- . entry ( pair. clone ( ) )
105+ . entry ( pair)
89106 . or_insert ( HashSet :: new ( ) )
90107 . insert ( tok. clone ( ) ) ;
91108 }
@@ -94,59 +111,117 @@ fn get_pairs(
94111 ( pair_to_count, pair_to_toks, heap)
95112}
96113
114+ fn rusty_get_chunk_pre_toks ( filepath : & str , start : u64 , end : u64 , special_tokens : Vec < String > ) -> io:: Result < HashMap < Vec < u8 > , usize > > {
115+ let mut tok_to_count: HashMap < Vec < u8 > , usize > = HashMap :: new ( ) ;
116+ let pat_special_toks = special_tokens. iter ( ) . map ( |x : & String | regex:: escape ( x) ) . collect :: < Vec < String > > ( ) . join ( "|" ) ;
117+ let re_special_toks: Regex = Regex :: new ( & pat_special_toks) . unwrap ( ) ;
118+ let mut file = File :: open ( filepath) ?;
119+ file. seek ( SeekFrom :: Start ( start) ) ?;
120+ let mut buffer = vec ! [ 0u8 ; ( end - start) as usize ] ;
121+ let bytes_read = file. read ( & mut buffer) ?;
122+ let chunk = String :: from_utf8_lossy ( & buffer[ ..bytes_read] ) ;
123+ for regex_match in re_special_toks. split ( & chunk) . flat_map ( |subchunk| RE . find_iter ( subchunk. unwrap ( ) ) ) {
124+ let key: Vec < u8 > = regex_match. unwrap ( ) . as_str ( ) . as_bytes ( ) . to_vec ( ) ;
125+ // += 1
126+ * tok_to_count. entry ( key) . or_default ( ) += 1 ;
127+ }
128+ Ok ( tok_to_count)
129+ }
97130
98- /// we take a map from vector of bytes and a max vocab size.
99- /// and we return?
100- #[ pyfunction]
101- fn rusty_merge ( mut tok_to_count : HashMap < Vec < Vec < u8 > > , usize > , max : usize ) -> PyResult < Vec < ( Vec < u8 > , Vec < u8 > ) > > {
102- println ! ( "we are inside rusty_merge" ) ;
103- // println!("{:?}", tok_to_count);
104- let mut max_pairs = vec ! [ ( vec![ 0 ; 0 ] , vec![ 0 ; 0 ] ) ; 0 ] ;
105- let ( mut pair_to_count, mut pair_to_toks, mut heap) = get_pairs ( & tok_to_count) ;
131+ fn rusty_get_pre_toks ( filepath : & str , boundaries : Vec < u64 > , special_tokens : Vec < String > ) -> io:: Result < ( HashMap < Vec < TokenId > , usize > , TokenInterner ) > {
132+ let token_to_id = HashMap :: new ( ) ;
133+ let tokens = Vec :: new ( ) ;
134+ let mut interner = TokenInterner { tokens, token_to_id} ;
135+ // let pat_special_toks = special_tokens.iter().map(|x: &String| regex::escape(x)).collect::<Vec<String>>().join("|");
136+ let r: Vec < ( u64 , u64 ) > = boundaries. windows ( 2 ) . map ( |x| ( x[ 0 ] , x[ 1 ] ) ) . collect ( ) ;
106137
107- // let max_count_2 = pair_to_count[&("w".as_bytes().to_vec(),"w".as_bytes().to_vec())];
108- // println!("max_count {}", max_count_2);
109- // TODO: maintain heap of max-pairs, instead of finding the max_pair on every loop.
138+ let intermediate = r
139+ . par_iter ( )
140+ . map ( |( start, end) | {
141+ rusty_get_chunk_pre_toks ( filepath, * start, * end, special_tokens. clone ( ) ) . unwrap ( )
142+ } )
143+ . collect :: < Vec < _ > > ( ) ;
144+
145+ let mut toks : HashSet < Vec < u8 > > = HashSet :: new ( ) ;
146+
147+ for chunkmap in & intermediate {
148+ for raw in chunkmap. keys ( ) {
149+ toks. insert ( raw. clone ( ) ) ;
150+ for byte in raw {
151+ toks. insert ( vec ! [ * byte] ) ;
152+ }
153+ }
154+ }
155+
156+ let mut sorted_toks: Vec < Vec < u8 > > = toks. into_iter ( ) . collect ( ) ;
157+ sorted_toks. sort ( ) ;
158+
159+ for tok in sorted_toks {
160+ interner. intern ( tok) ;
161+ }
162+
163+ let mut result = HashMap :: new ( ) ;
164+
165+ for chunk_map in intermediate {
166+ for ( raw, count) in chunk_map {
167+ let token_ids: Vec < TokenId > = raw
168+ . chunks ( 1 )
169+ . map ( |byte| interner. intern ( byte. to_vec ( ) ) )
170+ . collect ( ) ;
171+
172+ * result. entry ( token_ids) . or_default ( ) += count;
173+ }
174+ }
175+
176+ Ok ( ( result, interner) )
177+ }
178+
179+ fn rusty_merge ( mut tok_to_count : HashMap < Vec < TokenId > , usize > , max : usize , interner : & mut TokenInterner ) -> PyResult < Vec < ( Vec < u8 > , Vec < u8 > ) > > {
180+ let mut max_pairs = vec ! [ ( vec![ 0 ; 0 ] , vec![ 0 ; 0 ] ) ; 0 ] ;
181+ let ( mut pair_to_count, mut pair_to_toks, mut heap) = get_pairs ( & tok_to_count, interner) ;
110182
111183 while max_pairs. len ( ) < max {
112184 // pop from heap.
113185 if let Some ( heap_entry) = heap. pop ( ) {
114- if pair_to_count. get ( & heap_entry. pair ) == Some ( & heap_entry. count ) {
115- let max_pair = heap_entry. pair ;
116-
117- max_pairs. push ( max_pair. clone ( ) ) ;
186+ let max_pair = ( interner. intern ( heap_entry. pair . 0 ) , interner. intern ( heap_entry. pair . 1 ) ) ;
187+ if pair_to_count. get ( & max_pair) == Some ( & heap_entry. count ) {
118188
119- let max_pair_to_toks = & pair_to_toks [ & max_pair] ;
189+ max_pairs . push ( ( interner . get_bytes ( max_pair. 0 ) . to_vec ( ) , interner . get_bytes ( max_pair . 1 ) . to_vec ( ) ) ) ;
120190
191+ let tokens_to_process: Vec < Vec < TokenId > > = pair_to_toks[ & max_pair] . iter ( ) . cloned ( ) . collect ( ) ;
121192 // for every tok that contains max_pair
122- for tok in max_pair_to_toks . clone ( ) {
123- match tok_to_count. entry ( tok. clone ( ) ) {
193+ for tok in & tokens_to_process {
194+ match tok_to_count. entry ( tok. to_vec ( ) ) {
124195 Entry :: Occupied ( _) => ( ) ,
125196 Entry :: Vacant ( _) => continue , // Skip if doesn't exist
126197 } ;
127198
128- let tok_count = tok_to_count[ & tok. clone ( ) ] ;
199+ let tok_count = if let Some ( & count) = tok_to_count. get ( tok) {
200+ count
201+ } else {
202+ continue ;
203+ } ;
129204
130205 // for every pair in tok
131206 for i in 0 ..tok. len ( ) - 1 {
132- let pair = ( tok[ i] . clone ( ) , tok[ i+1 ] . clone ( ) ) ;
207+ let pair = ( tok[ i] , tok[ i+1 ] ) ;
133208
134- match pair_to_toks. entry ( pair. clone ( ) ) {
209+ match pair_to_toks. entry ( pair) {
135210 std:: collections:: hash_map:: Entry :: Occupied ( mut e) => {
136211 let set = e. get_mut ( ) ;
137212
138213 // remove from pair's toks.
139- set. remove ( & tok) ;
214+ set. remove ( tok) ;
140215 }
141216 std:: collections:: hash_map:: Entry :: Vacant ( _) => { }
142217 }
143218
144- match pair_to_count. entry ( pair. clone ( ) ) {
219+ match pair_to_count. entry ( pair) {
145220 std:: collections:: hash_map:: Entry :: Occupied ( mut e) => {
146221 * e. get_mut ( ) = e. get_mut ( ) . saturating_sub ( tok_count) ; // remove tok_count from pair count.
147222
148223 if * e. get ( ) > 0 {
149- heap. push ( PairHeapEntry { count : * e. get ( ) , pair } ) ;
224+ heap. push ( PairHeapEntry { count : * e. get ( ) , pair : ( interner . get_bytes ( pair . 0 ) . to_vec ( ) , interner . get_bytes ( pair . 1 ) . to_vec ( ) ) } ) ;
150225 } else {
151226 e. remove ( ) ;
152227 }
@@ -157,132 +232,70 @@ fn rusty_merge(mut tok_to_count: HashMap<Vec<Vec<u8>>, usize>, max: usize) -> Py
157232 }
158233
159234 // construct new_tok
160- let mut new_tok: Vec < Vec < u8 > > = Vec :: new ( ) ;
235+ let mut new_tok: Vec < TokenId > = Vec :: new ( ) ;
161236
162237 let mut i = 0 ;
163238
164239 while i < tok. len ( ) {
165- if i < tok. len ( ) - 1 && ( tok[ i] . clone ( ) , tok[ i+1 ] . clone ( ) ) == max_pair {
166- let new_vocab = [ & tok[ i] [ ..] , & tok[ i+1 ] [ ..] ] . concat ( ) ;
167- // println!("new_vocab {:?}", String::from_utf8(new_vocab.clone()));
168- new_tok. push ( new_vocab) ;
240+ if i < tok. len ( ) - 1 && ( tok[ i] , tok[ i+1 ] ) == max_pair {
241+
242+ let bytes1 = interner. get_bytes ( tok[ i] ) ;
243+ let bytes2 = interner. get_bytes ( tok[ i+1 ] ) ;
244+
245+ let merged_bytes = [ bytes1, bytes2] . concat ( ) ;
246+
247+ let new_token_id = interner. intern ( merged_bytes) ;
248+
249+ new_tok. push ( new_token_id) ;
169250 i += 2 ;
170251 } else {
171- new_tok. push ( tok[ i] . clone ( ) ) ;
252+ new_tok. push ( tok[ i] ) ;
172253 i += 1
173254 }
174255 }
175256
176- // println!("new_tok {:?}", new_tok.clone().iter().map(|x| String::from_utf8(x.clone()).unwrap()).collect::<Vec<String>>().join("") );
177-
178257 // increment new_tok count by tok_count
179258 * tok_to_count. entry ( new_tok. clone ( ) ) . or_default ( ) += tok_count;
180259
181260 // decrement tok count by tok_count
182261 // if tok_count is zero -> remove tok entry all together.
183- decrement_or_remove ( & mut tok_to_count, tok. clone ( ) , tok_count) ;
262+ decrement_or_remove ( & mut tok_to_count, tok. to_vec ( ) , tok_count) ;
184263
185264 // for every pair in new_tok
186- for i in 0 ..new_tok. clone ( ) . len ( ) - 1 {
187- let pair = ( new_tok[ i] . clone ( ) , new_tok[ i+1 ] . clone ( ) ) ;
265+ for i in 0 ..new_tok. len ( ) - 1 {
266+ let pair = ( new_tok[ i] , new_tok[ i+1 ] ) ;
267+
268+ pair_to_toks
269+ . entry ( pair)
270+ . or_insert_with ( HashSet :: new)
271+ . insert ( new_tok. clone ( ) ) ;
188272
189- match pair_to_toks. entry ( pair. clone ( ) ) {
190- // old_pair
191- std:: collections:: hash_map:: Entry :: Occupied ( mut e) => {
192- // insert new_tok
193- e. get_mut ( ) . insert ( new_tok. clone ( ) ) ;
194- }
195- // new pair
196- std:: collections:: hash_map:: Entry :: Vacant ( e) => {
197- e. insert ( HashSet :: from_iter ( [ new_tok. clone ( ) ] ) ) ;
198- }
199- }
200273
201- * pair_to_count. entry ( pair. clone ( ) ) . or_default ( ) += tok_count;
274+ * pair_to_count. entry ( pair) . or_default ( ) += tok_count;
202275
203276 heap. push ( PairHeapEntry {
204277 count : pair_to_count[ & pair] ,
205- pair : pair. clone ( ) ,
278+ pair : ( interner . get_bytes ( pair. 0 ) . to_vec ( ) , interner . get_bytes ( pair . 1 ) . to_vec ( ) ) ,
206279 } ) ;
207280
208281 }
209282 }
210283 }
211284 }
212285 } ;
213- // println!("{:?}", max_pairs);
214286 Ok ( max_pairs)
215287}
216288
217- #[ pyfunction]
218- fn rusty_full_merge ( filepath : & str , boundaries : Vec < u64 > , special_tokens : Vec < String > , max : usize ) -> PyResult < Vec < ( Vec < u8 > , Vec < u8 > ) > > {
219- let tok_to_count = rusty_get_pre_toks ( filepath, boundaries, special_tokens) ;
220- // println!("tok_to_count {:?}", tok_to_count);
221- return rusty_merge ( tok_to_count. unwrap ( ) , max)
222- }
223-
224-
225- #[ pyfunction]
226- fn rusty_pre_tok ( chunk : & str , special_tokens : Vec < String > ) -> HashMap < Vec < Vec < u8 > > , usize > {
227- let mut tok_to_count: HashMap < Vec < Vec < u8 > > , usize > = HashMap :: new ( ) ;
228- let pat_special_toks = special_tokens. iter ( ) . map ( |x : & String | regex:: escape ( x) ) . collect :: < Vec < String > > ( ) . join ( "|" ) ;
229- let re_special_toks: Regex = Regex :: new ( & pat_special_toks) . unwrap ( ) ;
230-
231- for regex_match in re_special_toks. split ( chunk) . flat_map ( |subchunk| RE . find_iter ( subchunk. unwrap ( ) ) ) {
232- let key: Vec < Vec < u8 > > = regex_match. unwrap ( ) . as_str ( ) . as_bytes ( ) . iter ( ) . map ( |& byte| vec ! [ byte] ) . collect ( ) ;
233-
234- // += 1
235- * tok_to_count. entry ( key) . or_default ( ) += 1 ;
236-
237- }
238-
239- tok_to_count
240- }
241-
242- fn rusty_get_chunk_pre_toks ( filepath : & str , start : u64 , end : u64 , special_tokens : Vec < String > ) -> io:: Result < HashMap < Vec < Vec < u8 > > , usize > > {
243- let mut tok_to_count: HashMap < Vec < Vec < u8 > > , usize > = HashMap :: new ( ) ;
244- let pat_special_toks = special_tokens. iter ( ) . map ( |x : & String | regex:: escape ( x) ) . collect :: < Vec < String > > ( ) . join ( "|" ) ;
245- let re_special_toks: Regex = Regex :: new ( & pat_special_toks) . unwrap ( ) ;
246- let mut file = File :: open ( filepath) ?;
247- file. seek ( SeekFrom :: Start ( start) ) ?;
248- let mut buffer = vec ! [ 0u8 ; ( end - start) as usize ] ;
249- let bytes_read = file. read ( & mut buffer) ?;
250- let chunk = String :: from_utf8_lossy ( & buffer[ ..bytes_read] ) ;
251- for regex_match in re_special_toks. split ( & chunk) . flat_map ( |subchunk| RE . find_iter ( subchunk. unwrap ( ) ) ) {
252- let key: Vec < Vec < u8 > > = regex_match. unwrap ( ) . as_str ( ) . as_bytes ( ) . iter ( ) . map ( |& byte| vec ! [ byte] ) . collect ( ) ;
253- // += 1
254- * tok_to_count. entry ( key) . or_default ( ) += 1 ;
255- }
256- Ok ( tok_to_count)
257- }
258289
259290#[ pyfunction]
260- fn rusty_get_pre_toks ( filepath : & str , boundaries : Vec < u64 > , special_tokens : Vec < String > ) -> io:: Result < HashMap < Vec < Vec < u8 > > , usize > > {
261-
262- // let pat_special_toks = special_tokens.iter().map(|x: &String| regex::escape(x)).collect::<Vec<String>>().join("|");
263- let r: Vec < ( u64 , u64 ) > = boundaries. windows ( 2 ) . map ( |x| ( x[ 0 ] , x[ 1 ] ) ) . collect ( ) ;
264- let map = r
265- . par_iter ( )
266- . map ( |( start, end) | {
267- rusty_get_chunk_pre_toks ( filepath, * start, * end, special_tokens. clone ( ) )
268- } )
269- . collect :: < Vec < _ > > ( )
270- . into_iter ( )
271- . fold ( HashMap :: new ( ) , |mut acc, chunk_map| {
272- for ( key, value) in chunk_map. unwrap ( ) {
273- * acc. entry ( key) . or_default ( ) += value;
274- }
275- acc
276- } ) ;
277- Ok ( map)
291+ fn rusty_full_merge ( filepath : & str , boundaries : Vec < u64 > , special_tokens : Vec < String > , max : usize ) -> PyResult < Vec < ( Vec < u8 > , Vec < u8 > ) > > {
292+ let ( tok_to_count, mut interner) = rusty_get_pre_toks ( filepath, boundaries, special_tokens) . unwrap ( ) ;
293+ return rusty_merge ( tok_to_count, max, & mut interner) ;
278294}
279295
280296/// A Python module implemented in Rust.
281297#[ pymodule]
282298fn rusty_tokey ( m : & Bound < ' _ , PyModule > ) -> PyResult < ( ) > {
283- m. add_function ( wrap_pyfunction ! ( rusty_merge, m) ?) ?;
284299 m. add_function ( wrap_pyfunction ! ( rusty_full_merge, m) ?) ?;
285- m. add_function ( wrap_pyfunction ! ( rusty_pre_tok, m) ?) ?;
286- m. add_function ( wrap_pyfunction ! ( rusty_get_pre_toks, m) ?) ?;
287300 Ok ( ( ) )
288301}
0 commit comments