11use super :: Optimizer ;
2- use crate :: memo:: { Memo , MergeGroupProduct , MergeProducts } ;
2+ use crate :: memo:: { Memo , MergeGoalProduct , MergeGroupProduct , MergeProducts } ;
33
44mod helpers;
55
66impl < M : Memo > Optimizer < M > {
77 /// Processes merge results by updating the task graph to reflect merges in the memo.
88 ///
9+ /// This function handles both group merges and goal merges by delegating to specialized
10+ /// handlers for each type of merge.
11+ ///
12+ /// # Parameters
13+ /// * `result` - The merge result to handle, containing information about merged groups and goals.
14+ ///
15+ /// # Returns
16+ /// * `Result<(), OptimizeError>` - Success or an error that occurred during processing.
17+ pub ( super ) async fn handle_merge_result (
18+ & mut self ,
19+ result : MergeProducts ,
20+ ) -> Result < ( ) , M :: MemoError > {
21+ self . handle_group_merges ( & result. group_merges ) . await ?;
22+ self . handle_goal_merges ( & result. goal_merges ) . await
23+ }
24+
25+ /// Handles merges of logical groups by updating the task graph.
26+ ///
927 /// When groups are merged in the memo, multiple exploration tasks may now refer to
1028 /// the same underlying group. This method handles the task graph updates required
1129 /// by such merges. For each merged group, it:
@@ -28,18 +46,18 @@ impl<M: Memo> Optimizer<M> {
2846 /// with it, containing all logical expressions from the original groups with no duplicates.
2947 ///
3048 /// # Parameters
31- /// * `result ` - The merge result to handle, containing information about merged groups .
49+ /// * `group_merges ` - A slice of group merge products to handle .
3250 ///
3351 /// # Returns
3452 /// * `Result<(), OptimizeError>` - Success or an error that occurred during processing.
35- pub ( super ) async fn handle_merge_result (
53+ async fn handle_group_merges (
3654 & mut self ,
37- result : MergeProducts ,
55+ group_merges : & [ MergeGroupProduct ] ,
3856 ) -> Result < ( ) , M :: MemoError > {
3957 for MergeGroupProduct {
4058 new_group_id,
4159 merged_groups,
42- } in result . group_merges
60+ } in group_merges
4361 {
4462 // For each merged group, get all group exploration tasks.
4563 // We don't need to check for the new group ID since it is guaranteed to be new.
@@ -49,7 +67,7 @@ impl<M: Memo> Optimizer<M> {
4967 . collect ( ) ;
5068
5169 if !group_explore_tasks. is_empty ( ) {
52- let all_logical_exprs = self . memo . get_all_logical_exprs ( new_group_id) . await ?;
70+ let all_logical_exprs = self . memo . get_all_logical_exprs ( * new_group_id) . await ?;
5371
5472 let ( principal_task_id, secondary_task_ids) =
5573 group_explore_tasks. split_first ( ) . unwrap ( ) ;
@@ -70,7 +88,64 @@ impl<M: Memo> Optimizer<M> {
7088
7189 // Step 4: Set the index to point to the new representative task.
7290 self . group_exploration_task_index
73- . insert ( new_group_id, * principal_task_id) ;
91+ . insert ( * new_group_id, * principal_task_id) ;
92+ }
93+ }
94+
95+ Ok ( ( ) )
96+ }
97+
98+ /// Handles merges of optimization goals by updating the task graph.
99+ ///
100+ /// When goals are merged in the memo, multiple optimization tasks may now refer to
101+ /// the same underlying goal. This method handles the task graph updates required
102+ /// by such merges. For each merged goal, it:
103+ ///
104+ /// 1. **Consolidates**: Merges all secondary tasks into a principal task by transferring
105+ /// their dependencies (incoming and outgoing optimization tasks and plan tasks) and
106+ /// updating references, ensuring a clean 1:1 mapping between goals and optimization tasks.
107+ ///
108+ /// 2. **Re-indexes**: Updates the goal optimization index to point to the principal task
109+ /// for the new goal ID.
110+ ///
111+ /// After processing, each merged goal will have exactly one optimization task associated
112+ /// with it, with all dependencies properly redirected to it.
113+ ///
114+ /// # Parameters
115+ /// * `goal_merges` - A slice of goal merge products to handle.
116+ ///
117+ /// # Returns
118+ /// * `Result<(), OptimizeError>` - Success or an error that occurred during processing.
119+ async fn handle_goal_merges (
120+ & mut self ,
121+ goal_merges : & [ MergeGoalProduct ] ,
122+ ) -> Result < ( ) , M :: MemoError > {
123+ for MergeGoalProduct {
124+ new_goal_id,
125+ merged_goals,
126+ } in goal_merges
127+ {
128+ // For each merged goal, get all goal optimization tasks.
129+ // We don't need to check for the new goal ID since it is guaranteed to be new.
130+ let goal_optimize_tasks: Vec < _ > = merged_goals
131+ . iter ( )
132+ . filter_map ( |goal_id| self . goal_optimization_task_index . get ( goal_id) . copied ( ) )
133+ . collect ( ) ;
134+
135+ if !goal_optimize_tasks. is_empty ( ) {
136+ let ( principal_task_id, secondary_task_ids) =
137+ goal_optimize_tasks. split_first ( ) . unwrap ( ) ;
138+
139+ // *NOTE*: Deduplication and updates of implementation tasks have already been
140+ // handled in the `handle_group_merges` method, so we don't need to do it here.
141+
142+ // Step 1: Consolidate all dependent tasks into the new "representative" task.
143+ self . consolidate_goal_optimize ( * principal_task_id, secondary_task_ids)
144+ . await ;
145+
146+ // Step 2: Set the index to point to the new representative task.
147+ self . goal_optimization_task_index
148+ . insert ( * new_goal_id, * principal_task_id) ;
74149 }
75150 }
76151
0 commit comments