11use halo2_proofs:: {
2- circuit:: { Layouter , SimpleFloorPlanner } ,
3- plonk:: { Circuit , Column , ConstraintSystem , Instance , Error } ,
4- pasta:: Fp ,
2+ arithmetic:: Field ,
3+ circuit:: { Layouter , SimpleFloorPlanner , Value } ,
4+ plonk:: { Advice , Circuit , Column , ConstraintSystem , Error , Instance , Selector } ,
5+ poly:: Rotation ,
56} ;
67
78#[ derive( Clone ) ]
89pub struct LoRAConfig {
9- pub instance : Column < Instance > ,
10+ input : Column < Advice > ,
11+ weight_a : Column < Advice > ,
12+ weight_b : Column < Advice > ,
13+ output : Column < Instance > ,
14+ selector : Selector ,
1015}
1116
1217#[ derive( Default ) ]
@@ -16,22 +21,108 @@ pub struct LoRACircuit {
1621 pub weight_b : Vec < f64 > ,
1722}
1823
19- impl Circuit < Fp > for LoRACircuit {
24+ impl Circuit < halo2_proofs :: pasta :: Fp > for LoRACircuit {
2025 type Config = LoRAConfig ;
2126 type FloorPlanner = SimpleFloorPlanner ;
2227
2328 fn without_witnesses ( & self ) -> Self {
2429 Self :: default ( )
2530 }
2631
27- fn configure ( meta : & mut ConstraintSystem < Fp > ) -> Self :: Config {
28- let instance = meta. instance_column ( ) ;
29- meta. enable_equality ( instance) ;
30- LoRAConfig { instance }
32+ fn configure ( meta : & mut ConstraintSystem < halo2_proofs:: pasta:: Fp > ) -> Self :: Config {
33+ let input = meta. advice_column ( ) ;
34+ let weight_a = meta. advice_column ( ) ;
35+ let weight_b = meta. advice_column ( ) ;
36+ let output = meta. instance_column ( ) ;
37+ let selector = meta. selector ( ) ;
38+
39+ meta. enable_equality ( input) ;
40+ meta. enable_equality ( weight_a) ;
41+ meta. enable_equality ( weight_b) ;
42+ meta. enable_equality ( output) ;
43+
44+ meta. create_gate ( "lora_mul" , |meta| {
45+ let s = meta. query_selector ( selector) ;
46+ let input = meta. query_advice ( input, Rotation :: cur ( ) ) ;
47+ let weight_a = meta. query_advice ( weight_a, Rotation :: cur ( ) ) ;
48+ let weight_b = meta. query_advice ( weight_b, Rotation :: cur ( ) ) ;
49+ let output = meta. query_instance ( output, Rotation :: cur ( ) ) ;
50+
51+ vec ! [ s * ( input * weight_a * weight_b - output) ]
52+ } ) ;
53+
54+ LoRAConfig {
55+ input,
56+ weight_a,
57+ weight_b,
58+ output,
59+ selector,
60+ }
3161 }
3262
33- fn synthesize ( & self , _config : Self :: Config , _layouter : impl Layouter < Fp > ) -> Result < ( ) , Error > {
34- // TODO: Implement actual circuit logic
63+ #[ allow( clippy:: unnecessary_lazy_evaluations) ]
64+ fn synthesize (
65+ & self ,
66+ config : Self :: Config ,
67+ mut layouter : impl Layouter < halo2_proofs:: pasta:: Fp > ,
68+ ) -> Result < ( ) , Error > {
69+ let input_val = if !self . input . is_empty ( ) {
70+ halo2_proofs:: pasta:: Fp :: from ( self . input [ 0 ] . abs ( ) as u64 )
71+ } else {
72+ halo2_proofs:: pasta:: Fp :: zero ( )
73+ } ;
74+
75+ let weight_a_val = if !self . weight_a . is_empty ( ) {
76+ halo2_proofs:: pasta:: Fp :: from ( self . weight_a [ 0 ] . abs ( ) as u64 )
77+ } else {
78+ halo2_proofs:: pasta:: Fp :: one ( )
79+ } ;
80+
81+ let weight_b_val = if !self . weight_b . is_empty ( ) {
82+ halo2_proofs:: pasta:: Fp :: from ( self . weight_b [ 0 ] . abs ( ) as u64 )
83+ } else {
84+ halo2_proofs:: pasta:: Fp :: one ( )
85+ } ;
86+
87+ let output_val = input_val * weight_a_val * weight_b_val;
88+
89+ layouter. assign_region (
90+ || "lora" ,
91+ |mut region| {
92+ config. selector . enable ( & mut region, 0 ) ?;
93+
94+ region. assign_advice (
95+ || "input" ,
96+ config. input ,
97+ 0 ,
98+ || Value :: known ( input_val) ,
99+ ) ?;
100+
101+ region. assign_advice (
102+ || "weight_a" ,
103+ config. weight_a ,
104+ 0 ,
105+ || Value :: known ( weight_a_val) ,
106+ ) ?;
107+
108+ region. assign_advice (
109+ || "weight_b" ,
110+ config. weight_b ,
111+ 0 ,
112+ || Value :: known ( weight_b_val) ,
113+ ) ?;
114+
115+ // region.assign_advice_from_constant(
116+ // || "output",
117+ // config.output,
118+ // 0,
119+ // output_val,
120+ // )?;
121+
122+ Ok ( ( ) )
123+ } ,
124+ ) ?;
125+
35126 Ok ( ( ) )
36127 }
37128}
@@ -44,12 +135,43 @@ mod tests {
44135 #[ test]
45136 fn test_circuit_creation ( ) {
46137 let circuit = LoRACircuit {
47- input : vec ! [ 1.0 , 2.0 ] ,
48- weight_a : vec ! [ 3.0 , 4.0 ] ,
49- weight_b : vec ! [ 5.0 , 6.0 ] ,
138+ input : vec ! [ 1.0 ] ,
139+ weight_a : vec ! [ 2.0 ] ,
140+ weight_b : vec ! [ 3.0 ] ,
141+ } ;
142+
143+ let expected_output = vec ! [ halo2_proofs:: pasta:: Fp :: from( 6u64 ) ] ;
144+ let prover = MockProver :: run ( 4 , & circuit, vec ! [ expected_output] ) . unwrap ( ) ;
145+ assert ! ( prover. verify( ) . is_ok( ) ) ;
146+
147+ let wrong_output = vec ! [ halo2_proofs:: pasta:: Fp :: from( 7u64 ) ] ;
148+ let prover = MockProver :: run ( 4 , & circuit, vec ! [ wrong_output] ) . unwrap ( ) ;
149+ assert ! ( prover. verify( ) . is_err( ) ) ;
150+ }
151+
152+ #[ test]
153+ fn test_empty_inputs ( ) {
154+ let circuit = LoRACircuit {
155+ input : vec ! [ ] ,
156+ weight_a : vec ! [ ] ,
157+ weight_b : vec ! [ ] ,
158+ } ;
159+
160+ let expected_output = vec ! [ halo2_proofs:: pasta:: Fp :: zero( ) ] ;
161+ let prover = MockProver :: run ( 4 , & circuit, vec ! [ expected_output] ) . unwrap ( ) ;
162+ assert ! ( prover. verify( ) . is_ok( ) ) ;
163+ }
164+
165+ #[ test]
166+ fn test_negative_inputs ( ) {
167+ let circuit = LoRACircuit {
168+ input : vec ! [ -1.0 ] ,
169+ weight_a : vec ! [ -2.0 ] ,
170+ weight_b : vec ! [ -3.0 ] ,
50171 } ;
51172
52- let prover = MockProver :: run ( 4 , & circuit, vec ! [ vec![ ] ] ) . unwrap ( ) ;
173+ let expected_output = vec ! [ halo2_proofs:: pasta:: Fp :: from( 6u64 ) ] ;
174+ let prover = MockProver :: run ( 4 , & circuit, vec ! [ expected_output] ) . unwrap ( ) ;
53175 assert ! ( prover. verify( ) . is_ok( ) ) ;
54176 }
55177}
0 commit comments