11use std:: { cmp:: Ordering , collections:: BTreeMap } ;
22
3- use ff_ext:: ExtensionField ;
4- use itertools:: { Itertools , chain, izip} ;
5- use multilinear_extensions:: { Expression , Fixed , ToExpr , WitnessId , rlc_chip_record} ;
6- use p3_field:: FieldAlgebra ;
7-
83use crate :: {
9- LookupTable ,
4+ LookupTable , RAMType ,
105 evaluation:: EvalExpression ,
116 gkr:: layer:: { Layer , LayerType , ROTATION_OPENING_COUNT } ,
127} ;
8+ use ceno_emul:: Tracer ;
9+ use ff_ext:: ExtensionField ;
10+ use itertools:: { Itertools , chain, izip} ;
11+ use multilinear_extensions:: { Expression , Fixed , ToExpr , WitnessId , rlc_chip_record} ;
12+ use p3_field:: FieldAlgebra ;
1313
1414#[ derive( Clone , Debug , Default ) ]
1515pub struct RotationParams < E : ExtensionField > {
@@ -39,6 +39,9 @@ pub struct LayerConstraintSystem<E: ExtensionField> {
3939 pub xor_lookups : Vec < Expression < E > > ,
4040 pub range_lookups : Vec < Expression < E > > ,
4141
42+ pub ram_read : Vec < Expression < E > > ,
43+ pub ram_write : Vec < Expression < E > > ,
44+
4245 // global challenge
4346 pub alpha : Expression < E > ,
4447 pub beta : Expression < E > ,
@@ -66,6 +69,8 @@ impl<E: ExtensionField> LayerConstraintSystem<E> {
6669 and_lookups : vec ! [ ] ,
6770 xor_lookups : vec ! [ ] ,
6871 range_lookups : vec ! [ ] ,
72+ ram_read : vec ! [ ] ,
73+ ram_write : vec ! [ ] ,
6974 alpha,
7075 beta,
7176 }
@@ -89,29 +94,31 @@ impl<E: ExtensionField> LayerConstraintSystem<E> {
8994 self . expr_names . push ( name) ;
9095 }
9196
92- fn rlc_table_lookup (
93- & self ,
94- lookup_table : LookupTable ,
95- values : impl Iterator < Item = Expression < E > > ,
96- ) -> Expression < E > {
97- rlc_chip_record (
98- chain ! (
99- std:: iter:: once( E :: BaseField :: from_canonical_u64( lookup_table as u64 ) . expr( ) ) ,
100- values
101- )
102- . collect_vec ( ) ,
97+ pub fn lookup_and8 ( & mut self , a : Expression < E > , b : Expression < E > , c : Expression < E > ) {
98+ let rlc_record = rlc_chip_record (
99+ vec ! [
100+ E :: BaseField :: from_canonical_u64( LookupTable :: And as u64 ) . expr( ) ,
101+ a,
102+ b,
103+ c,
104+ ] ,
103105 self . alpha . clone ( ) ,
104106 self . beta . clone ( ) ,
105- )
106- }
107-
108- pub fn lookup_and8 ( & mut self , a : Expression < E > , b : Expression < E > , c : Expression < E > ) {
109- let rlc_record = self . rlc_table_lookup ( LookupTable :: And , [ a, b, c] . iter ( ) . cloned ( ) ) ;
107+ ) ;
110108 self . and_lookups . push ( rlc_record) ;
111109 }
112110
113111 pub fn lookup_xor8 ( & mut self , a : Expression < E > , b : Expression < E > , c : Expression < E > ) {
114- let rlc_record = self . rlc_table_lookup ( LookupTable :: Xor , [ a, b, c] . iter ( ) . cloned ( ) ) ;
112+ let rlc_record = rlc_chip_record (
113+ vec ! [
114+ E :: BaseField :: from_canonical_u64( LookupTable :: Xor as u64 ) . expr( ) ,
115+ a,
116+ b,
117+ c,
118+ ] ,
119+ self . alpha . clone ( ) ,
120+ self . beta . clone ( ) ,
121+ ) ;
115122 self . xor_lookups . push ( rlc_record) ;
116123 }
117124
@@ -120,17 +127,90 @@ impl<E: ExtensionField> LayerConstraintSystem<E> {
120127 /// `value << (16 - size)`.
121128 pub fn lookup_range ( & mut self , value : Expression < E > , size : usize ) {
122129 assert ! ( size <= 16 ) ;
123- self . range_lookups
124- . push ( self . rlc_table_lookup ( LookupTable :: U16 , vec ! [ value. clone( ) ] . into_iter ( ) ) ) ;
130+ let rlc_record = rlc_chip_record (
131+ vec ! [
132+ E :: BaseField :: from_canonical_u64( LookupTable :: U16 as u64 ) . expr( ) ,
133+ value. clone( ) ,
134+ ] ,
135+ self . alpha . clone ( ) ,
136+ self . beta . clone ( ) ,
137+ ) ;
138+ self . range_lookups . push ( rlc_record) ;
125139 if size < 16 {
126- self . range_lookups . push (
127- self . rlc_table_lookup (
128- LookupTable :: U16 ,
129- [ value * E :: BaseField :: from_canonical_u64 ( 1 << ( 16 - size) ) . expr ( ) ]
130- . iter ( )
131- . cloned ( ) ,
132- ) ,
133- )
140+ let rlc_record = rlc_chip_record (
141+ vec ! [
142+ E :: BaseField :: from_canonical_u64( LookupTable :: U16 as u64 ) . expr( ) ,
143+ value * E :: BaseField :: from_canonical_u64( 1 << ( 16 - size) ) . expr( ) ,
144+ ] ,
145+ self . alpha . clone ( ) ,
146+ self . beta . clone ( ) ,
147+ ) ;
148+ self . range_lookups . push ( rlc_record)
149+ }
150+ }
151+
152+ /// records RAM write operations into the `ram_write` trace vector using RLC encoding.
153+ ///
154+ /// this function appends one RLC-encoded record per word written to memory,
155+ /// starting from `mem_start_addr`. Each record includes:
156+ /// - The operation type (assumed to be `RAMType::Memory`)
157+ /// - The memory address of the word (adjusted by `4 * index`)
158+ /// - The value being written
159+ /// - The timestamp of the write
160+ pub fn ram_write_record (
161+ & mut self ,
162+ mem_start_addr : Expression < E > ,
163+ values : Vec < Expression < E > > ,
164+ cur_ts : Expression < E > ,
165+ ) {
166+ for ( idx, value) in values. into_iter ( ) . enumerate ( ) {
167+ let rlc_record = rlc_chip_record (
168+ vec ! [
169+ E :: BaseField :: from_canonical_u64( RAMType :: Memory as u64 ) . expr( ) ,
170+ mem_start_addr. clone( )
171+ // `4` is num bytes per word
172+ // TODO fetch from constant
173+ + E :: BaseField :: from_canonical_u64( ( 4 * idx) as u64 ) . expr( ) ,
174+ value,
175+ cur_ts. clone( ) + E :: BaseField :: from_canonical_u64( Tracer :: SUBCYCLE_MEM ) . expr( ) ,
176+ ] ,
177+ self . alpha . clone ( ) ,
178+ self . beta . clone ( ) ,
179+ ) ;
180+ self . ram_write . push ( rlc_record) ;
181+ }
182+ }
183+
184+ /// records RAM read operations into the `ram_read` trace vector using RLC encoding.
185+ ///
186+ /// this function appends one RLC-encoded record per word written to memory,
187+ /// starting from `mem_start_addr`. Each record includes:
188+ /// - The operation type (assumed to be `RAMType::Memory`)
189+ /// - The memory address of the word (adjusted by `4 * index`)
190+ /// - The value being read
191+ /// - The ts corresponding to each value
192+ pub fn ram_read_record (
193+ & mut self ,
194+ mem_start_addr : Expression < E > ,
195+ values : Vec < Expression < E > > ,
196+ ts : Vec < Expression < E > > ,
197+ ) {
198+ assert_eq ! ( values. len( ) , ts. len( ) ) ;
199+ for ( idx, ( value, ts) ) in izip ! ( values, ts) . enumerate ( ) {
200+ let rlc_record = rlc_chip_record (
201+ vec ! [
202+ E :: BaseField :: from_canonical_u64( RAMType :: Memory as u64 ) . expr( ) ,
203+ mem_start_addr. clone( )
204+ // `4` is num bytes per word
205+ // TODO fetch from constant
206+ + E :: BaseField :: from_canonical_u64( ( 4 * idx) as u64 ) . expr( ) ,
207+ value,
208+ ts,
209+ ] ,
210+ self . alpha . clone ( ) ,
211+ self . beta . clone ( ) ,
212+ ) ;
213+ self . ram_read . push ( rlc_record) ;
134214 }
135215 }
136216
@@ -270,8 +350,27 @@ impl<E: ExtensionField> LayerConstraintSystem<E> {
270350 layer_name : String ,
271351 in_expr_evals : Vec < usize > ,
272352 n_challenges : usize ,
353+ ram_write_evals : impl ExactSizeIterator < Item = ( Option < Expression < E > > , usize ) > ,
354+ ram_read_evals : impl ExactSizeIterator < Item = ( Option < Expression < E > > , usize ) > ,
273355 lookup_evals : impl ExactSizeIterator < Item = ( Option < Expression < E > > , usize ) > ,
274356 ) -> Layer < E > {
357+ // process ram read/write record
358+ assert_eq ! ( ram_write_evals. len( ) , self . ram_write. len( ) , ) ;
359+ assert_eq ! ( ram_read_evals. len( ) , self . ram_read. len( ) , ) ;
360+
361+ for ( idx, ram_expr, ram_eval) in izip ! (
362+ 0 ..,
363+ chain!( self . ram_write. clone( ) , self . ram_read. clone( ) , ) ,
364+ ram_write_evals. chain( ram_read_evals)
365+ ) {
366+ self . add_non_zero_constraint (
367+ ram_expr - E :: BaseField :: ONE . expr ( ) , // ONE is for padding
368+ ( ram_eval. 0 , EvalExpression :: Single ( ram_eval. 1 ) ) ,
369+ format ! ( "round 0th: {idx}th ram read/write operation" ) ,
370+ ) ;
371+ }
372+
373+ // process lookup records
275374 assert_eq ! (
276375 lookup_evals. len( ) ,
277376 self . and_lookups. len( ) + self . xor_lookups. len( ) + self . range_lookups. len( )
@@ -336,6 +435,7 @@ impl<E: ExtensionField> LayerConstraintSystem<E> {
336435 is_linear_so_far && t. is_linear ( )
337436 } ) ;
338437
438+ // process evaluation group by eq expression
339439 let mut eq_map = BTreeMap :: new ( ) ;
340440 izip ! (
341441 evals. into_iter( ) ,
0 commit comments