@@ -61,18 +61,17 @@ where
6161 . push ( objective) ;
6262 }
6363
64- // Validate that each agent has at least one objective for each milestone year
65- for ( agent_id, _agent ) in agents {
64+ // Check that agents have appropriate objectives for their decision rule every year
65+ for ( agent_id, agent ) in agents {
6666 let agent_objectives = objectives
6767 . get ( agent_id)
6868 . with_context ( || format ! ( "Agent {} has no objectives" , agent_id) ) ?;
6969 for & year in milestone_years {
70- ensure ! (
71- agent_objectives. iter( ) . any( |obj| obj. year == year) ,
72- "Agent {} is missing objectives for milestone year {}" ,
73- agent_id,
74- year
75- ) ;
70+ let objectives_for_year: Vec < _ > = agent_objectives
71+ . iter ( )
72+ . filter ( |obj| obj. year == year)
73+ . collect ( ) ;
74+ check_agent_objectives ( & objectives_for_year, & agent. decision_rule , agent_id, year) ?;
7675 }
7776 }
7877
@@ -109,18 +108,67 @@ fn check_objective_parameter(
109108 match decision_rule {
110109 DecisionRule :: Single => {
111110 check_field_none ! ( decision_weight) ;
111+ check_field_none ! ( decision_lexico_order) ;
112112 }
113113 DecisionRule :: Weighted => {
114114 check_field_some ! ( decision_weight) ;
115+ check_field_none ! ( decision_lexico_order) ;
115116 }
116117 DecisionRule :: Lexicographical { tolerance : _ } => {
117118 check_field_none ! ( decision_weight) ;
119+ check_field_some ! ( decision_lexico_order) ;
118120 }
119121 } ;
120122
121123 Ok ( ( ) )
122124}
123125
126+ /// Check that a set of objectives meets the requirements of a decision rule
127+ fn check_agent_objectives (
128+ objectives : & [ & AgentObjective ] ,
129+ decision_rule : & DecisionRule ,
130+ agent_id : & str ,
131+ year : u32 ,
132+ ) -> Result < ( ) > {
133+ let count = objectives. len ( ) ;
134+ match decision_rule {
135+ DecisionRule :: Single => {
136+ ensure ! (
137+ count == 1 ,
138+ "Agent {} has {} objectives for milestone year {} but should have exactly 1" ,
139+ agent_id,
140+ count,
141+ year
142+ ) ;
143+ }
144+ DecisionRule :: Weighted => {
145+ ensure ! (
146+ count > 1 ,
147+ "Agent {} has {} objectives for milestone year {} but should have more than 1" ,
148+ agent_id,
149+ count,
150+ year
151+ ) ;
152+ }
153+ DecisionRule :: Lexicographical { tolerance : _ } => {
154+ let mut lexico_orders: Vec < u32 > = objectives
155+ . iter ( )
156+ . filter_map ( |obj| obj. decision_lexico_order )
157+ . collect ( ) ;
158+ lexico_orders. sort_unstable ( ) ;
159+ ensure ! (
160+ lexico_orders == [ 1 , 2 ] ,
161+ "Agent {} must have objectives with decision_lexico_order values of 1 and 2 for milestone year {}, but found {:?}" ,
162+ agent_id,
163+ year,
164+ lexico_orders
165+ ) ;
166+ }
167+ }
168+
169+ Ok ( ( ) )
170+ }
171+
124172#[ cfg( test) ]
125173mod tests {
126174 use super :: * ;
@@ -132,35 +180,42 @@ mod tests {
132180 #[ test]
133181 fn test_check_objective_parameter ( ) {
134182 macro_rules! objective {
135- ( $decision_weight: expr) => {
183+ ( $decision_weight: expr, $decision_lexico_order : expr ) => {
136184 AgentObjective {
137185 agent_id: "agent" . into( ) ,
138186 year: 2020 ,
139187 objective_type: ObjectiveType :: EquivalentAnnualCost ,
140188 decision_weight: $decision_weight,
189+ decision_lexico_order: $decision_lexico_order,
141190 }
142191 } ;
143192 }
144193
145194 // DecisionRule::Single
146195 let decision_rule = DecisionRule :: Single ;
147- let objective = objective ! ( None ) ;
196+ let objective = objective ! ( None , None ) ;
148197 assert ! ( check_objective_parameter( & objective, & decision_rule) . is_ok( ) ) ;
149- let objective = objective ! ( Some ( 1.0 ) ) ;
198+ let objective = objective ! ( Some ( 1.0 ) , None ) ;
199+ assert ! ( check_objective_parameter( & objective, & decision_rule) . is_err( ) ) ;
200+ let objective = objective ! ( None , Some ( 1 ) ) ;
150201 assert ! ( check_objective_parameter( & objective, & decision_rule) . is_err( ) ) ;
151202
152203 // DecisionRule::Weighted
153204 let decision_rule = DecisionRule :: Weighted ;
154- let objective = objective ! ( Some ( 1.0 ) ) ;
205+ let objective = objective ! ( Some ( 1.0 ) , None ) ;
155206 assert ! ( check_objective_parameter( & objective, & decision_rule) . is_ok( ) ) ;
156- let objective = objective ! ( None ) ;
207+ let objective = objective ! ( None , None ) ;
208+ assert ! ( check_objective_parameter( & objective, & decision_rule) . is_err( ) ) ;
209+ let objective = objective ! ( None , Some ( 1 ) ) ;
157210 assert ! ( check_objective_parameter( & objective, & decision_rule) . is_err( ) ) ;
158211
159212 // DecisionRule::Lexicographical
160213 let decision_rule = DecisionRule :: Lexicographical { tolerance : 1.0 } ;
161- let objective = objective ! ( None ) ;
214+ let objective = objective ! ( None , Some ( 1 ) ) ;
162215 assert ! ( check_objective_parameter( & objective, & decision_rule) . is_ok( ) ) ;
163- let objective = objective ! ( Some ( 1.0 ) ) ;
216+ let objective = objective ! ( None , None ) ;
217+ assert ! ( check_objective_parameter( & objective, & decision_rule) . is_err( ) ) ;
218+ let objective = objective ! ( Some ( 1.0 ) , None ) ;
164219 assert ! ( check_objective_parameter( & objective, & decision_rule) . is_err( ) ) ;
165220 }
166221
@@ -199,6 +254,7 @@ mod tests {
199254 year : 2020 ,
200255 objective_type : ObjectiveType :: EquivalentAnnualCost ,
201256 decision_weight : None ,
257+ decision_lexico_order : None ,
202258 } ;
203259 let expected = [ ( "agent" . into ( ) , vec ! [ objective. clone( ) ] ) ]
204260 . into_iter ( )
@@ -228,6 +284,7 @@ mod tests {
228284 year : 2020 ,
229285 objective_type : ObjectiveType :: EquivalentAnnualCost ,
230286 decision_weight : Some ( 1.0 ) , // Should only accept None for DecisionRule::Single
287+ decision_lexico_order : None ,
231288 } ;
232289 assert ! ( read_agent_objectives_from_iter(
233290 [ bad_objective] . into_iter( ) ,
@@ -236,4 +293,43 @@ mod tests {
236293 )
237294 . is_err( ) ) ;
238295 }
296+
297+ #[ test]
298+ fn test_check_agent_objectives ( ) {
299+ let objective1 = AgentObjective {
300+ agent_id : "agent" . into ( ) ,
301+ year : 2020 ,
302+ objective_type : ObjectiveType :: EquivalentAnnualCost ,
303+ decision_weight : None ,
304+ decision_lexico_order : Some ( 1 ) ,
305+ } ;
306+ let objective2 = AgentObjective {
307+ agent_id : "agent" . into ( ) ,
308+ year : 2020 ,
309+ objective_type : ObjectiveType :: EquivalentAnnualCost ,
310+ decision_weight : None ,
311+ decision_lexico_order : Some ( 2 ) ,
312+ } ;
313+
314+ // DecisionRule::Single
315+ let decision_rule = DecisionRule :: Single ;
316+ let objectives = [ & objective1] ;
317+ assert ! ( check_agent_objectives( & objectives, & decision_rule, "agent" , 2020 ) . is_ok( ) ) ;
318+ let objectives = [ & objective1, & objective2] ;
319+ assert ! ( check_agent_objectives( & objectives, & decision_rule, "agent" , 2020 ) . is_err( ) ) ;
320+
321+ // DecisionRule::Weighted
322+ let decision_rule = DecisionRule :: Weighted ;
323+ let objectives = [ & objective1, & objective2] ;
324+ assert ! ( check_agent_objectives( & objectives, & decision_rule, "agent" , 2020 ) . is_ok( ) ) ;
325+ let objectives = [ & objective1] ;
326+ assert ! ( check_agent_objectives( & objectives, & decision_rule, "agent" , 2020 ) . is_err( ) ) ;
327+
328+ // DecisionRule::Lexicographical
329+ let decision_rule = DecisionRule :: Lexicographical { tolerance : 1.0 } ;
330+ let objectives = [ & objective1, & objective2] ;
331+ assert ! ( check_agent_objectives( & objectives, & decision_rule, "agent" , 2020 ) . is_ok( ) ) ;
332+ let objectives = [ & objective1, & objective1] ;
333+ assert ! ( check_agent_objectives( & objectives, & decision_rule, "agent" , 2020 ) . is_err( ) ) ;
334+ }
239335}
0 commit comments