diff --git a/agpl-cr-assignment.adb b/agpl-cr-assignment.adb index 6b6ac74..f27bba1 100644 --- a/agpl-cr-assignment.adb +++ b/agpl-cr-assignment.adb @@ -92,7 +92,8 @@ package body Agpl.Cr.Assignment is function Empty_Object return Object is begin - return (Cr.Agent.Maps.Empty_Map, True); + return (Agents => Cr.Agent.Maps.Empty_Map, + Ok => True); end Empty_Object; ----------------- @@ -465,7 +466,7 @@ package body Agpl.Cr.Assignment is ------------------------ function Invalid_Assignment return Object is - This : Object; + This : Object := (others => <>); begin This.Ok := False; return This; diff --git a/agpl-cr-assignment.ads b/agpl-cr-assignment.ads index 62fc398..ad0f784 100644 --- a/agpl-cr-assignment.ads +++ b/agpl-cr-assignment.ads @@ -130,10 +130,10 @@ package Agpl.Cr.Assignment is private type Object is tagged record - Agents : Agent.Maps.Map; - Ok : Boolean := True; -- An assignment can be invalid. + + Agents : Agent.Maps.Map; end record; end Agpl.Cr.Assignment; diff --git a/agpl-cr-cost_matrix.adb b/agpl-cr-cost_matrix.adb index 0e2b0f9..dfdeea3 100644 --- a/agpl-cr-cost_matrix.adb +++ b/agpl-cr-cost_matrix.adb @@ -62,6 +62,7 @@ package body Agpl.Cr.Cost_Matrix is while A /= AL.No_Element loop Ini := TL.First (Tasks); while Ini /= TL.No_Element loop + Fin := TL.First (Tasks); while Fin /= TL.No_Element loop @@ -92,14 +93,37 @@ package body Agpl.Cr.Cost_Matrix is Htn.Tasks.Get_Id (Tl.Element (Ini)), Htn.Tasks.Get_Id (Tl.Element (Fin)))); end if; + else + -- Same task... may be should be Infinite, maybe zero? + pragma Ummmm; + null; end if; TL.Next (Fin); end loop; + TL.Next (Ini); end loop; AL.Next (A); end loop; + + -- Add the No_Task specials + A := Agents.First; + while Al.Has_Element (A) loop + Ini := Tasks.First; + while Tl.Has_Element (Ini) loop + + Set_Cost (This, + Cr.Agent.Get_Name (Al.Element (A)), + Htn.Tasks.Get_Id (Tl.Element (Ini)), + Htn.Tasks.No_Task, + 0.0); + + Tl.Next (Ini); + end loop; + Al.Next (A); + end loop; + end Create; ------------ @@ -258,6 +282,28 @@ package body Agpl.Cr.Cost_Matrix is This.Matrix.Iterate (Do_It'Access); end Print; + ----------------- + -- Print_Diffs -- + ----------------- + + procedure Print_Diffs (L, R : in Object) is + I : Cursor := L.Matrix.First; + begin + Log ("DIFFS IN COST MATRIX", Always); + while Has_Element (I) loop + if not R.Matrix.Contains (Key (I)) then + Log ("Missing key: " & Key (I), Always); + elsif Element (I) /= Element (R.Matrix.Find (Key (I))) then + Log (Key (I) & ":" & Element (I)'Img & " /= " & + Key (I) & ":" & Element (R.Matrix.Find (Key (I)))'Img, + Always); + end if; + + Next (I); + end loop; + Log ("END DIFFS", Always); + end Print_Diffs; + -------------- -- Set_Cost -- -------------- @@ -269,14 +315,6 @@ package body Agpl.Cr.Cost_Matrix is Fin : in Htn.Tasks.Task_Id; Cost : in Costs) is - function Rounded (C : in Costs) return Costs is - begin - if C /= Infinite then - return Costs (Float'Floor (Float (C * 100.0)) / 100.0); - else - return C; - end if; - end Rounded; begin Include (This.Matrix, Key (Agent, Ini, Fin), Cost); end Set_Cost; diff --git a/agpl-cr-cost_matrix.ads b/agpl-cr-cost_matrix.ads index 889da4e..b1bbc74 100644 --- a/agpl-cr-cost_matrix.ads +++ b/agpl-cr-cost_matrix.ads @@ -117,6 +117,8 @@ package Agpl.Cr.Cost_Matrix is procedure Print (This : in Object); -- Debug dump + procedure Print_Diffs (L, R : in Object); + private package ATT_Maps is new Ada.Containers.Indefinite_Hashed_Maps diff --git a/agpl-cr-mutable_assignment.adb b/agpl-cr-mutable_assignment.adb index c3df621..064ce67 100644 --- a/agpl-cr-mutable_assignment.adb +++ b/agpl-cr-mutable_assignment.adb @@ -36,13 +36,14 @@ with Agpl.Cr.Assigner.Greedy_Minmax_Exhaustive; with Agpl.Cr.Plan_Assigner; with Agpl.Cr.Plan_Assigner.Greedy1; with Agpl.Cr.Tasks.Insertions; -with Agpl.Htn.Plan_Node; with Agpl.Htn.Plan.Utils; with Agpl.Htn.Plan.Utils.Random; +with Agpl.Htn.Plan_Node; with Agpl.Htn.Tasks.Maps; with Agpl.Random; with Agpl.Trace; use Agpl.Trace; +with Ada.Containers; with Ada.Numerics.Elementary_Functions; with Ada.Numerics.Generic_Elementary_Functions; @@ -52,6 +53,7 @@ package body Agpl.Cr.Mutable_Assignment is File : constant String := "[Mutable_Assignment] "; + use type Ada.Containers.Count_Type; use type Htn.Tasks.Task_Id; use type Optimization.Cost; use type Optimization.Annealing.Probability; @@ -90,6 +92,7 @@ package body Agpl.Cr.Mutable_Assignment is procedure Add_Agent (This : in out Object; A : in Cr.Agent.Object'Class) is begin This.Context.Ref.all.Agents.Include (A.Get_Name, A); + This.Set_Assignment (This.To_Assignment, This.Context.Ref.Criterion); end Add_Agent; ------------------ @@ -134,6 +137,49 @@ package body Agpl.Cr.Mutable_Assignment is end; end Add_Mutation; + --------------------- + -- Add_Or_Contexts -- + --------------------- + + procedure Add_Or_Contexts (This : in out Object; + Node : in Htn.Plan.Subplan) + is + use Htn.Plan_Node; + Parent : constant Htn.Plan_Node.Node_Access := Get_Parent (Node); + begin + if Parent /= null then + if Get_Kind (Parent) = Or_Node then + if not This.Contexts.Contains (Or_Key (Get_Id (Parent))) then + -- Check for duplicates, since we can arrive at an OR node + -- from several tasks (children of an AND node underlying. + declare + C : Or_Context := (Solution_Context with + Node => Parent, + Branch => Node); + begin + This.Add_To_Bag (C, All_Active_Or_Nodes); + This.Contexts.Insert (C.Key, C); + end; + else + -- We may need to update the OR branch if it's the first time + -- reaching it + declare + Ctx_Ptr : constant Solution_Context_Ptr := + This.Ptr (Or_Key (Get_Id (Parent))); + C : Or_Context renames Or_Context (Ctx_Ptr.all); + begin + if C.Branch /= Node then + C.Branch := Node; + Log ("Add_Or_Nodes: Corrected Or Branch taken.", + Debug, Detail_Section); + end if; + end; + end if; + end if; + This.Add_Or_Contexts (Parent); + end if; + end Add_Or_Contexts; + ---------------- -- Add_To_Bag -- ---------------- @@ -161,7 +207,7 @@ package body Agpl.Cr.Mutable_Assignment is procedure Add_Undo_Move (This : in Object; Job : in Task_Context_Ptr; - Undo : in out Undo_Info) + Undo : in out Undo_Internal) is begin Undo.Move_Stack.Append @@ -296,6 +342,7 @@ package body Agpl.Cr.Mutable_Assignment is begin This.Create_Empty_Bag (All_Assigned_Tasks); This.Create_Empty_Bag (All_Agents); + This.Create_Empty_Bag (All_Active_Or_Nodes); end Create_Empty_Bags; -------------------------- @@ -378,13 +425,15 @@ package body Agpl.Cr.Mutable_Assignment is -------------------- procedure Do_Heuristic_1 (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info) + Undo : out Undo_Info) is + U : Undo_Internal (From_Scratch); A : Cr.Assignment.Object := This.To_Assignment; begin - Undo.Ass := A; - Desc := +"Heuristic 1"; + U.Ass := A; + U.Description := +"Heuristic 1"; + Undo.Handle.Set (U); + declare use Cr.Assignment; New_Assignment : constant Cr.Assignment.Object := @@ -410,18 +459,20 @@ package body Agpl.Cr.Mutable_Assignment is -------------------- procedure Do_Heuristic_2 (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info) + Undo : out Undo_Info) is + U : Undo_Internal (From_Scratch); begin - Undo.Ass := This.To_Assignment; - Desc := +"Heuristic 2"; + U.Ass := This.To_Assignment; + U.Description := +"Heuristic 2"; + Undo.Handle.Set (U); + declare use Cr.Assignment; New_Assignment : constant Cr.Assignment.Object := Plan_Assigner.Greedy1.Assign ((Plan_Assigner.Object with null record), - Get_Agents_Without_Tasks (Undo.Ass), + Get_Agents_Without_Tasks (U.Ass), This.Context.Ref.Plan, This.Context.Ref.Costs, This.Context.Ref.Criterion); @@ -446,26 +497,23 @@ package body Agpl.Cr.Mutable_Assignment is ---------------------- procedure Do_Agent_Reorder (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info) + Undo : out Undo_Info) is + U : Undo_Internal (From_Scratch); Agent : constant Agent_Id := Agent_Id (+Agent_Context (This.Select_Random_Context (All_Agents).all).Agent_Name); begin - Desc := +"AGENT REORDER Nē"; --- Log ("Reordering agent", Always); --- This.To_Assignment.Print_Assignment; - declare New_Ass : Assignment.Object := This.To_Assignment; Ag : Cr.Agent.Object'Class := New_Ass.Get_Agent (String (Agent)); Tasks : Task_Lists.List := Ag.Get_Tasks; - U : Undo_Info; begin - U.Ass := New_Ass; - Undo := U; + U.Description := +"AGENT REORDER Nē"; + U.Ass := New_Ass; + Undo.Handle.Set (U); + Ag.Clear_Tasks; while not Tasks.Is_Empty loop declare @@ -479,7 +527,7 @@ package body Agpl.Cr.Mutable_Assignment is Cd, Ct, Ok); if not Ok then Log ("Failed to reorder agent tasks", Warning, Log_Section); - This.Do_Identity (Desc, Undo); + This.Do_Identity (Undo); return; else Ag := New_Ag.Get; @@ -499,13 +547,13 @@ package body Agpl.Cr.Mutable_Assignment is ----------------- procedure Do_Identity (This : in out Object; - Desc : out Ustring; - Undo : out Undo_Info) + Undo : out Undo_Info) is pragma Unreferenced (This); + U : Undo_Internal := (Identity, +"IDENTITY"); begin - Desc := +"Identity"; - Undo := (others => <>); + Log ("Identity mutation performed", Debug, Detail_Section); + Undo.Handle.Set (U); end Do_Identity; -------------------- @@ -554,13 +602,13 @@ package body Agpl.Cr.Mutable_Assignment is Log ("Inserting " & Src_Ptr.Job'Img & " after" & Src_Ptr.Prev'Img & - " and before" & Src_Ptr.Next'Img, - Debug, Detail_Section); + " and before" & Src_Ptr.Next'Img & + " owned by " & String (New_Owner), + Debug, Detail_Section); declare Cost : Costs; begin - pragma Remove_Expensive_Check; if Expensive_Checks then Reevaluate_Agent_Cost (This, New_Owner, Cost); if Acm.Element (This.Agent_Costs.Find (New_Owner)) /= Cost then @@ -601,16 +649,16 @@ package body Agpl.Cr.Mutable_Assignment is --------------------- procedure Do_Auction_Task (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info) + Undo : out Undo_Info) is + U : Undo_Internal (Move_Task); begin if This.Num_Assigned_Tasks <= 1 then - Do_Identity (This, Desc, Undo); + This.Do_Identity (Undo); return; end if; - Desc := + "LOG AUCTION"; + U.Description := + "LOG AUCTION"; declare use Ada.Numerics.Elementary_Functions; @@ -625,7 +673,8 @@ package body Agpl.Cr.Mutable_Assignment is Best_Cost : Costs := Infinite; Best_Name : Ustring; begin - This.Add_Undo_Move (Src, Undo); + This.Add_Undo_Move (Src, U); + Undo.Handle.Set (U); This.Do_Remove_Task (Src); Log ("Checking" & Checks'Img & " of" & This.Num_Assigned_Tasks'Img & @@ -667,7 +716,7 @@ package body Agpl.Cr.Mutable_Assignment is Src_Copy, This.Get_Task_Context (Src_Copy.Next), Get_Attribute (Src_Copy, Owner)); - This.Do_Identity (Desc, Undo); + This.Do_Identity (Undo); end if; end; end Do_Auction_Task; @@ -677,16 +726,16 @@ package body Agpl.Cr.Mutable_Assignment is -------------------------------- procedure Do_Exhaustive_Auction_Task (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info) + Undo : out Undo_Info) is + U : Undo_Internal (Move_Task); begin if This.Num_Assigned_Tasks <= 1 then - Do_Identity (This, Desc, Undo); + This.Do_Identity (Undo); return; end if; - Desc := + "FULL AUCTION"; + U.Description := + "FULL AUCTION"; declare use Ada.Numerics.Elementary_Functions; @@ -728,7 +777,8 @@ package body Agpl.Cr.Mutable_Assignment is end Do_It; begin - This.Add_Undo_Move (Src, Undo); + This.Add_Undo_Move (Src, U); + Undo.Handle.Set (U); This.Do_Remove_Task (Src); This.Contexts.Iterate (Do_It'Access); @@ -743,7 +793,7 @@ package body Agpl.Cr.Mutable_Assignment is Src_Copy, This.Get_Task_Context (Src_Copy.Next), Get_Attribute (Src_Copy, Owner)); - This.Do_Identity (Desc, Undo); + This.Do_Identity (Undo); end if; end; end Do_Exhaustive_Auction_Task; @@ -753,16 +803,16 @@ package body Agpl.Cr.Mutable_Assignment is ---------------------------- procedure Do_Guided_Auction_Task (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info) + Undo : out Undo_Info) is + U : Undo_Internal (Move_Task); begin if This.Num_Assigned_Tasks <= 1 then - Do_Identity (This, Desc, Undo); + This.Do_Identity (Undo); return; end if; - Desc := + "GUIDED+LOG AUCTION"; + U.Description := + "GUIDED+LOG AUCTION"; declare use Ada.Numerics.Elementary_Functions; @@ -781,7 +831,8 @@ package body Agpl.Cr.Mutable_Assignment is Best_Cost : Costs := Infinite; Best_Name : Ustring; begin - This.Add_Undo_Move (Src, Undo); + This.Add_Undo_Move (Src, U); + Undo.Handle.Set (U); This.Do_Remove_Task (Src); Log ("Checking" & Checks'Img & " of" & This.Num_Assigned_Tasks'Img & @@ -802,12 +853,12 @@ package body Agpl.Cr.Mutable_Assignment is (Curr_Prev, Src_Copy.Job, Curr_Next, - Agent_Id (Get_Attribute (Curr_Target, Owner))); + Best_Agent); if Curr_Cost < Best_Cost then Best_Cost := Curr_Cost; Best_Prev := Curr_Prev; Best_Next := Curr_Next; - Best_Name := +String (Get_Attribute (Curr_Target, Owner)); + Best_Name := +String (Best_Agent); end if; end; end loop; @@ -821,7 +872,7 @@ package body Agpl.Cr.Mutable_Assignment is Src_Copy, This.Get_Task_Context (Src_Copy.Next), Get_Attribute (Src_Copy, Owner)); - This.Do_Identity (Desc, Undo); + This.Do_Identity (Undo); end if; end; end Do_Guided_Auction_Task; @@ -831,21 +882,22 @@ package body Agpl.Cr.Mutable_Assignment is ------------------ procedure Do_Move_Task (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info) + Undo : out Undo_Info) is Src : Task_Context_Ptr := This.Select_Random_Task (All_Assigned_Tasks); Src_Copy : Task_Context := Task_Context (Src.all); + U : Undo_Internal (Move_Task); begin if This.Num_Assigned_Tasks <= 1 then - Do_Identity (This, Desc, Undo); + This.Do_Identity (Undo); return; end if; - Desc := + "MOVE"; + U.Description := + "MOVE"; - This.Add_Undo_Move (Src, Undo); + This.Add_Undo_Move (Src, U); + Undo.Handle.Set (U); This.Do_Remove_Task (Src); declare @@ -875,16 +927,16 @@ package body Agpl.Cr.Mutable_Assignment is --------------------------------- procedure Do_Move_Task_Changing_Owner (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info) + Undo : out Undo_Info) is + U : Undo_Internal (Move_Task); begin if This.Num_Assigned_Tasks <= 1 then - Do_Identity (This, Desc, Undo); + This.Do_Identity (Undo); return; end if; - Desc := + "MOVE+OWNER"; + U.Description := + "MOVE+OWNER"; declare Src : Task_Context_Ptr := @@ -898,7 +950,8 @@ package body Agpl.Cr.Mutable_Assignment is (All_Agents).all).Agent_Name); begin - This.Add_Undo_Move (Src, Undo); + This.Add_Undo_Move (Src, U); + Undo.Handle.Set (U); This.Do_Remove_Task (Src); declare @@ -922,16 +975,16 @@ package body Agpl.Cr.Mutable_Assignment is ---------------------------------------- procedure Do_Guided_Move_Task_Changing_Owner (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info) + Undo : out Undo_Info) is + U : Undo_Internal (Move_Task); begin if This.Num_Assigned_Tasks <= 1 then - Do_Identity (This, Desc, Undo); + This.Do_Identity (Undo); return; end if; - Desc := + "MOVE+GUIDED+OWNER"; + U.Description := + "MOVE+GUIDED+OWNER"; declare Worst_Agent : constant Agent_Id := @@ -945,7 +998,8 @@ package body Agpl.Cr.Mutable_Assignment is (This.Select_Random_Context (All_Agents).all).Agent_Name); begin - This.Add_Undo_Move (Src, Undo); + This.Add_Undo_Move (Src, U); + Undo.Handle.Set (U); This.Do_Remove_Task (Src); declare @@ -969,15 +1023,16 @@ package body Agpl.Cr.Mutable_Assignment is ------------------- procedure Do_Swap_Order (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info) is + Undo : out Undo_Info) + is + U : Undo_Internal (Move_Task); begin if This.Num_Assigned_Tasks <= 1 then - Do_Identity (This, Desc, Undo); + This.Do_Identity (Undo); return; end if; - Desc := + "SWAP ORDER"; + U.Description := + "SWAP ORDER"; declare Src : Task_Context_Ptr := @@ -987,14 +1042,15 @@ package body Agpl.Cr.Mutable_Assignment is This.Get_Task_Context (Src.Next); begin if Next /= null then - This.Add_Undo_Move (Src, Undo); + This.Add_Undo_Move (Src, U); + Undo.Handle.Set (U); This.Do_Remove_Task (Src); This.Do_Insert_Task (Next, Src_Copy, This.Get_Task_Context (Next.Next), Agent_Id (Get_Attribute (Next, Owner))); else - Do_Identity (This, Desc, Undo); + This.Do_Identity (Undo); end if; end; end Do_Swap_Order; @@ -1004,15 +1060,16 @@ package body Agpl.Cr.Mutable_Assignment is ------------------- procedure Do_Swap_Tasks (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info) is + Undo : out Undo_Info) + is + U : Undo_Internal (Move_Task); begin if This.Num_Assigned_Tasks <= 2 then - Do_Identity (This, Desc, Undo); + This.Do_Identity (Undo); return; end if; - Desc := + "SWAP ANY"; + U.Description := + "SWAP ANY"; declare Src : Task_Context_Ptr := @@ -1023,7 +1080,8 @@ package body Agpl.Cr.Mutable_Assignment is Owner_1 : constant Agent_Id := Agent_Id (Get_Attribute (Src, Owner)); begin - This.Add_Undo_Move (Src, Undo); + This.Add_Undo_Move (Src, U); + Undo.Handle.Set (U); This.Do_Remove_Task (Src); declare @@ -1056,7 +1114,8 @@ package body Agpl.Cr.Mutable_Assignment is New_Next, New_Owner); - This.Add_Undo_Move (Target, Undo); + This.Add_Undo_Move (Target, U); + Undo.Handle.Set (U); This.Do_Remove_Task (Target); if Prev_1 /= No_Task then @@ -1085,6 +1144,186 @@ package body Agpl.Cr.Mutable_Assignment is end; end Do_Swap_Tasks; + ---------------------- + -- Descend_Removing -- + ---------------------- + + procedure Descend_Removing (This : in out Object; + Node : in Htn.Plan.Subplan; + Undo : in out Undo_Internal) is + use Htn.Plan_Node; + begin + Log ("Descending into " & Get_Kind (Node)'Img & " " & Get_Id (Node), + Debug, Detail_Section); + case Get_Kind (Node) is + when Task_Node => + if Get_Expanded (Node) then + -- Compound, do nothing and go down: + This.Descend_Removing (Get_Expansion (Node), Undo); + else + -- Remove the task + context + Log ("Removing its primitive task " & + Get_Task (Node).all.To_String, + Debug, Detail_Section); + declare + Tc : Task_Context_Ptr := + This.Get_Task_Context (Get_Task (Node).all.Get_Id); + begin + Undo.Or_Stack.Append ((Was_Before => Tc.Next, + Moved_One => Tc.Job, + Was_After => Tc.Prev, + Owner_Was => +Get_Attribute (Tc, Owner), + Minsum_Was => This.Minsum)); + This.Do_Remove_Task (Tc); + end; + end if; + when And_Node => + declare + Children : constant Node_Vectors.Vector := + Get_Children (Node); + begin + for I in Children.First_Index .. Children.Last_Index loop + This.Descend_Removing (Children.Element (I), Undo); + end loop; + end; + when Or_Node => + -- We must remove its context and go down its active branch + declare + Branch_Ptr : constant Solution_Context_Ptr := + This.Ptr (Or_Key (Get_Id (Node))); + begin + This.Descend_Removing (Or_Context (Branch_Ptr.all).Branch, Undo); + This.Remove_Context (Or_Key (Get_Id (Node))); + end; + end case; + end Descend_Removing; + + ----------------------- + -- Do_Switch_Or_Node -- + ----------------------- + + procedure Do_Switch_Or_Node (This : in out Object; + Undo : out Undo_Info) + is + U : Undo_Internal (Switch_Or_Node); + + Placeholder_Usable : Boolean := False; + Placeholder : Undo_Move_Task_Info; + + procedure Descend_Adding (Node : in Htn.Plan.Subplan) is + use Htn.Plan_Node; + begin + case Get_Kind (Node) is + when Task_Node => + if Get_Expanded (Node) then + -- Compound, do nothing: + Descend_Adding (Get_Expansion (Node)); + else + -- Create a new task and insert at random + declare + Tc : Task_Context; + begin + Tc.Job := Get_Task (Node).all.Get_Id; + if Placeholder_Usable then + Placeholder_Usable := False; + This.Do_Insert_Task + (This.Get_Task_Context (Placeholder.Was_After), + Tc, + This.Get_Task_Context (Placeholder.Was_Before), + + Placeholder.Owner_Was); + else + declare + Target : constant Task_Context_Ptr := + This.Select_Random_Task (All_Assigned_Tasks); + begin + This.Do_Insert_Task + (This.Get_Task_Context (Target.Prev), + Tc, + Target, + Get_Attribute (Target, Owner)); + end; + end if; + end; + end if; + when And_Node => + declare + Children : constant Node_Vectors.Vector := + Get_Children (Node); + begin + for I in Children.First_Index .. Children.Last_Index loop + Descend_Adding (Children.Element (I)); + end loop; + end; + when Or_Node => + declare + Oc : Or_Context := + (Solution_Context with + Node => Node, + Branch => Node_Vectors.Element + (Get_Children (Node), + (Random.Get_Integer + (Node_Vectors.First_Index (Get_Children (Node)), + Node_Vectors.Last_Index (Get_Children (Node)))))); + begin + This.Add_To_Bag (Oc, All_Active_Or_Nodes); + This.Contexts.Insert (Oc.Key, Oc); + end; + end case; + end Descend_Adding; + + use Htn.Plan_Node; + Target : constant Solution_Context_Ptr := + This.Select_Random_Context (All_Active_Or_Nodes); + Ctx : Or_Context renames Or_Context (Target.all); + + Children : Node_Vectors.Vector := Get_Children (Ctx.Node); + begin + if Children.Length <= 1 then + This.Do_Identity (Undo); + return; + end if; + + U.Description := + "SWITCH OR-NODE"; + + Log ("GOING TO SWITCH", Never); + + loop + declare + New_Child : constant Htn.Plan.Subplan := + Children.Element + (Random.Get_Integer + (Children.First_Index, Children.Last_Index)); + pragma Unbounded_Time; + begin + if New_Child /= Ctx.Branch then + U.Actived_Or_Branch := New_Child; + This.Descend_Removing (Ctx.Branch, U); + + -- in 50% ocassions, we reuse the place were the switched task was: + if Random.Get_Integer (0, 1) = 1 then + Placeholder_Usable := True; + Placeholder := U.Or_Stack.Vector (U.Or_Stack.First); + end if; + + Descend_Adding (New_Child); + Ctx.Branch := New_Child; + exit; + end if; + end; + end loop; + + Undo.Handle.Set (U); + + if Expensive_Checks and then not This.Is_Sane then + raise Program_Error; + end if; + exception + when others => + This.Context.Ref.Plan.Print_Tree_Summary; + This.Debug_Dump_Contexts; + raise; + end Do_Switch_Or_Node; + -------------------- -- Do_Remove_Task -- -------------------- @@ -1095,9 +1334,10 @@ package body Agpl.Cr.Mutable_Assignment is Agent : constant Agent_Id := Agent_Id (Get_Attribute (Job, Owner)); begin Log ("Removing " & Job.Job'Img & - " after" & Job.Prev'Img & - " and before" & Job.Next'Img, - Debug, Detail_Section); + " after" & Job.Prev'Img & + " and before" & Job.Next'Img & + " owned by " & Get_Attribute (Job, Owner), + Debug, Detail_Section); -- Costs to be updated declare @@ -1116,17 +1356,14 @@ package body Agpl.Cr.Mutable_Assignment is This.Adjust_Chain_Removing (Job); end; - -- Remove from bags - Remove_From_All_Bags (This, Job); - - This.Contexts.Delete (Task_Key (Job.Job)); + -- Remove from earth surface + This.Remove_Context (Task_Key (Job.Job)); Job := null; declare Cost : Costs; begin - pragma Remove_Expensive_Check; if Expensive_Checks then Reevaluate_Agent_Cost (This, Agent, Cost); if Acm.Element (This.Agent_Costs.Find (Agent)) /= Cost then @@ -1150,6 +1387,16 @@ package body Agpl.Cr.Mutable_Assignment is return This.Evaluate (This.Context.Ref.Criterion); end Evaluate; + function Evaluate (This : in Object) return Optimization.Cost is + C : constant Cr.Costs := This.Evaluate; + begin + if C = Cr.Infinite then + return Optimization.Infinite; + else + return Optimization.Cost (C); + end if; + end Evaluate; + -------------- -- Evaluate -- -------------- @@ -1170,7 +1417,11 @@ package body Agpl.Cr.Mutable_Assignment is function Evaluate_Minimax (This : in Object) return Costs is begin if This.Valid then - return This.MinMax.Last_Element.Cost; + if This.Minmax.Is_Empty then + return 0.0; + else + return This.Minmax.Last_Element.Cost; + end if; else return Infinite; end if; @@ -1251,6 +1502,18 @@ package body Agpl.Cr.Mutable_Assignment is function Is_Sane (This : in Object) return Boolean is + procedure Check_Or_Parents (Node : in Htn.Plan.Subplan) is + use Htn.Plan_Node; + begin + if Get_Kind (Node) = Or_Node and then + not This.Contexts.Contains (Or_Key (Get_Id (Node))) + then + This.Context.Ref.Plan.Print_Tree_Summary; + This.Debug_Dump_Contexts; + raise Program_Error with "Missing OR ancestor for some task!"; + end if; + end Check_Or_Parents; + procedure Check_Contexts (I : Solution_Context_Maps.Cursor) is X : constant Solution_Context'Class := Solution_Context_Maps.Element (I); @@ -1266,10 +1529,32 @@ package body Agpl.Cr.Mutable_Assignment is if Get_Finished (Node) or else Get_Expanded (Node) then raise Constraint_Error with "Compound or finished task is assigned"; + else + Check_Or_Parents (Node); end if; end; elsif X in Agent_Context then null; + elsif X in Or_Context then + -- Check the chosen branch is there + declare + use Htn.Plan_Node; + Oc : Or_Context renames Or_Context (X); + begin + if Get_Kind (Oc.Branch) = Task_Node and then + not Get_Expanded (Oc.Branch) + then + if not This.Contexts.Contains + (Task_Key (Get_Task (Oc.Branch).all.Get_Id)) + then + Log ("Missing task context for task " & + Get_Task (Oc.Branch).all.Get_Id'Img & + "; branch taken of " & Get_Id (Oc.Node), + Always, Log_Section); + raise Program_Error with "Missing task for OR branch"; + end if; + end if; + end; else raise Constraint_Error with "Unexpected context kind: " & External_Tag (X'Tag); @@ -1301,7 +1586,7 @@ package body Agpl.Cr.Mutable_Assignment is function Last_Mutation (This : in Object) return String is begin - return +This.Last_Mutation_Description; + return +This.Last_Mutation; end Last_Mutation; ----------------------------- @@ -1338,33 +1623,54 @@ package body Agpl.Cr.Mutable_Assignment is -- This.Debug_Dump_Contexts; -- Log ("** Minsum is " & To_String (This.Minsum), Always); + if not This.Valid then + Log ("Attempt to mutate an invalid solution!", Error, Log_Section); + raise Constraint_Error with "Attempt to mutate an invalid solution!"; + end if; + + if This.Context.Ref.Agents.Is_Empty or else + This.Context.Ref.Plan.Is_Empty + then + Log ("Empty plan, mutating to identity", Debug, Detail_Section); + This.Was_Valid := This.Valid; + This.Undoer := Undo_Identity'Access; + This.Do_Identity (This.Undo); + This.Last_Mutation := This.Undo.Handle.Ref.Description; + return; + end if; + + ------------------------------------ + for I in M.First .. M.Last loop if Luck <= M.Vector (I).Prob then - Log ("Performing mutation" & I'Img, - Debug, Section => Detail_Section); - Reset (This.Last_Mutation_Undo); - This.Last_Mutation_Index := I; - This.Last_Mutation_Exists := True; - This.Last_Mutation_Undo.Was_Valid := This.Valid; + This.Was_Valid := This.Valid; - -- This.Debug_Dump_Contexts; + Log ("Performing mutation" & I'Img & "; Valid: " & This.Valid'Img, + Debug, Section => Detail_Section); - M.Vector (I).Doer (This, - This.Last_Mutation_Description, - This.Last_Mutation_Undo); + This.Undoer := M.Vector (I).Undoer; + M.Vector (I).Doer (This, This.Undo); + This.Last_Mutation := This.Undo.Handle.Ref.Description; - Log ("Mutated: " & (+This.Last_Mutation_Description), + Log ("Mutated: " & (+This.Undo.Handle.Get.Description) & + "; Valid: " & This.Valid'Img, Debug, Section => Detail_Section); - -- This.Debug_Dump_Contexts; - -- Log ("** Minsum is " & To_String (This.Minsum), Always); + if Expensive_Checks then declare Old_Cost : constant Costs := This.Evaluate (This.Context.Ref.Criterion); + Valid : constant Boolean := This.Valid; begin - pragma Remove_This_Expensive_Check; This.Reevaluate_Costs; - pragma Assert (Old_Cost = This.Evaluate (This.Context.Ref.Criterion)); + if Old_Cost /= This.Evaluate (This.Context.Ref.Criterion) then + Log ("Manual cost (" & Valid'Img & "):" & Old_Cost'Img & + " should equal reeval" & + This.Evaluate (This.Context.Ref.Criterion)'Img & + " (Valid: " & This.Valid'Img & ")", + Always, Log_Section); + raise Program_Error with "Mismatch after mutation"; + end if; end; end if; return; @@ -1394,6 +1700,8 @@ package body Agpl.Cr.Mutable_Assignment is begin if New_Cost < Old_Cost then return Acceptability'Last; + elsif New_Cost = 0.0 then + return Acceptability'First; else return (Acceptability (Old_Cost / New_Cost) * @@ -1596,13 +1904,13 @@ package body Agpl.Cr.Mutable_Assignment is This.Agent_Costs.Insert (Id, Cost); end Ev; begin + -- Don't touch the Valid flag. It could be valid or not at this point. + Reevaluate_Minsum (This, This.Minsum); This.MinMax.Clear; This.Agent_Costs.Clear; Agent_Maps.Iterate (This.Context.Ref.Agents, Ev'Access); - - This.Valid := This.MinSum < Infinite; end Reevaluate_Costs; ------------------------ @@ -1648,7 +1956,6 @@ package body Agpl.Cr.Mutable_Assignment is ------------------ -- O (n) or worse (depending on the heuristic used). procedure Remove_Agent (This : in out Object; Name : in Agent_Id) is - Dummy_Desc : Ustring; Dummy_Undo : Undo_Info; C : Static_Context_Access renames This.Context.Ref; begin @@ -1659,10 +1966,23 @@ package body Agpl.Cr.Mutable_Assignment is else Reassign_Tasks (This, Name, Agent_Maps.First_Element (C.Agents).Get_Name); - Do_Heuristic_1 (This, Dummy_Desc, Dummy_Undo); + This.Do_Heuristic_1 (Dummy_Undo); end if; end Remove_Agent; + -------------------- + -- Remove_Context -- + -------------------- + + procedure Remove_Context (This : in out Object; + Key : in Solution_Context_Key) + is + Ptr : constant Solution_Context_Ptr := This.Ptr (Key); + begin + This.Remove_From_All_Bags (Ptr); + This.Contexts.Delete (Key); + end Remove_Context; + -------------------------- -- Remove_From_All_Bags -- -------------------------- @@ -1731,7 +2051,7 @@ package body Agpl.Cr.Mutable_Assignment is ----------- procedure Reset (This : in out Undo_Info) is - Empty_Undo : Undo_Info; + Empty_Undo : constant Undo_Info := (others => <>); begin This := Empty_Undo; end Reset; @@ -1886,6 +2206,10 @@ package body Agpl.Cr.Mutable_Assignment is This.Contexts.Insert (Solution_Context_Key (Task_Key (C.Job)), C); + -- Create OR contexts + This.Add_Or_Contexts + (This.Context.Ref.Plan.Get_Node (C.Job)); + Next (J); end; end loop; @@ -1931,7 +2255,9 @@ package body Agpl.Cr.Mutable_Assignment is New_New_Ass : Cr.Assignment.Object; Success : Boolean; begin - -- Cr.Cost_Matrix.Print (This.Context.Ref.Costs); +-- Cr.Cost_Matrix.Print (This.Context.Ref.Costs); +-- Log (This.Context.Ref.Agents.Length'Img, Always); +-- Log (Htn.Tasks.Maps.First_Element (Pending_Tasks).To_String, Always); Tasks.Insertions.Greedy (New_Ass, Htn.Tasks.Maps.First_Element (Pending_Tasks), @@ -1945,7 +2271,8 @@ package body Agpl.Cr.Mutable_Assignment is To_String (Integer (Pending_Tasks.First_Element.Get_Id)) & "-" & Pending_Tasks.First_Element.To_String, Error, Log_Section); - raise Constraint_Error; + This.Valid := False; + return; else Log ("Set_Assignment: assigned task " & To_String (Integer (Pending_Tasks.First_Element.Get_Id)) & @@ -1992,6 +2319,19 @@ package body Agpl.Cr.Mutable_Assignment is C : constant Static_Context_Access := This.Context.Ref; begin C.Costs := Costs; + + -- Equip our agents with 0.00 cost for No_Task --> No_Task. + -- This is needed elsewhere. + declare + procedure Do_It (I : Agent_Maps.Cursor) is + begin + Cr.Cost_Matrix.Set_Cost (C.Costs, Agent_Maps.Element (I).Get_Name, + No_Task, No_Task, 0.0); + end Do_It; + begin + C.Agents.Iterate (Do_It'Access); + end; + Reevaluate_Costs (This); end Set_Costs; @@ -2026,12 +2366,17 @@ package body Agpl.Cr.Mutable_Assignment is --------------- procedure Set_Tasks (This : in out Object; - Plan : in Htn.Plan.Object) + Plan : in Htn.Plan.Object; + Assign : in Boolean := True) is C : Static_Context_Access renames This.Context.Ref; begin Clear_Dynamic_Part (This); C.Plan := Htn.Plan.Inflate (Plan); + if Assign then + This.Set_Assignment (This.To_Assignment, + This.Context.Ref.Criterion); + end if; end Set_Tasks; -------------- @@ -2107,12 +2452,10 @@ package body Agpl.Cr.Mutable_Assignment is procedure Undo (This : in out Object) is begin - if This.Last_Mutation_Exists then - This.Context.Ref.Mutations.Vector - (This.Last_Mutation_Index).Undoer (This, This.Last_Mutation_Undo); - This.Valid := This.Last_Mutation_Undo.Was_Valid; - Reset (This.Last_Mutation_Undo); - This.Last_Mutation_Exists := False; + if This.Undo.Handle.Is_Valid then + This.Undoer (This, This.Undo); + This.Valid := This.Was_Valid; + This.Undo.Handle.Clear; else raise Constraint_Error with "No mutation performed to be undone"; end if; @@ -2127,7 +2470,15 @@ package body Agpl.Cr.Mutable_Assignment is is begin Log ("Undoing from scratch", Debug, Section => Detail_Section); - Set_Assignment (This, Undo.Ass, This.Context.Ref.Criterion); + case Undo.Handle.Get.Kind is + when From_Scratch => + This.Set_Assignment (Undo.Handle.Get.Ass, + This.Context.Ref.Criterion); + when Identity => + null; + when others => + raise Program_Error; + end case; end Undo_From_Scratch; ------------------- @@ -2145,37 +2496,104 @@ package body Agpl.Cr.Mutable_Assignment is -------------------- procedure Undo_Move_Task (This : in out Object; Undo : in Undo_Info) is - U : Undo_Info := Undo; + U : Undo_Internal renames Undo.Handle.Ref.all; begin - while not U.Move_Stack.Is_Empty loop - declare - Move : Undo_Move_Task_Info renames - U.Move_Stack.Vector (U.Move_Stack.Last); - Src : Task_Context_Ptr := This.Get_Task_Context (Move.Moved_One); - begin - This.Do_Move_Task - (After_This => This.Get_Task_Context (Move.Was_After), - Src => Src, - Before_This => This.Get_Task_Context (Move.Was_Before), - New_Owner => Agent_Id (+Move.Owner_Was)); - - if Move.Minsum_Was /= This.Minsum then - Log ("Cost was " & To_String (Move.Minsum_Was, 10) & - " but is " & To_String (This.Minsum, 10) & - " (" & To_String (This.Minsum - Move.Minsum_Was, 10) & ")", - Error, Log_Section); - -- Cr.Cost_Matrix.Print (This.Context.Ref.Costs); - This.Reevaluate_Costs; - -- raise Program_Error with "Undo breached cost integrity!"; - end if; - end; - U.Move_Stack.Delete (U.Move_Stack.Last); - end loop; + case U.Kind is + when Identity => + null; + when Move_Task => + for I in reverse U.Move_Stack.First .. U.Move_Stack.Last loop + declare + Move : Undo_Move_Task_Info renames U.Move_Stack.Vector (I); + Src : Task_Context_Ptr := + This.Get_Task_Context (Move.Moved_One); + begin + This.Do_Move_Task + (After_This => This.Get_Task_Context (Move.Was_After), + Src => Src, + Before_This => This.Get_Task_Context (Move.Was_Before), + New_Owner => Agent_Id (+Move.Owner_Was)); + + if Move.Minsum_Was /= This.Minsum then + Log ("Cost was " & To_String (Move.Minsum_Was, 10) & + " but is " & To_String (This.Minsum, 10) & + " (" & To_String (This.Minsum - Move.Minsum_Was, 10) & ")", + Error, Log_Section); + -- Cr.Cost_Matrix.Print (This.Context.Ref.Costs); + This.Reevaluate_Costs; + -- raise Program_Error with "Undo breached cost integrity!"; + end if; + end; + end loop; + when others => + raise Program_Error; + end case; end Undo_Move_Task; + ----------------- + -- Undo_Switch -- + ----------------- + + procedure Undo_Switch (This : in out Object; Undo : in Undo_Info) is +-- procedure Descend_Adding (Node : Htn.Subplan) is +-- begin +-- -- Re-add necessary or-nodes +-- -- Re-insert necessary tasks. +-- end Descend_Adding; + Dummy_Undo : Undo_Internal (Switch_Or_Node); + U : Undo_Internal renames Undo.Handle.Ref.all; + begin + Log ("UNDOING SWITCH", Debug, Detail_Section); + case U.Kind is + when Identity => + null; + when Switch_Or_Node => + This.Descend_Removing (U.Actived_Or_Branch, Dummy_Undo); + + -- Add all tasks: + for I in reverse U.Or_Stack.First .. U.Or_Stack.Last loop + declare + Tc : Task_Context; + begin + Tc.Job := U.Or_Stack.Vector (I).Moved_One; + This.Do_Insert_Task + (After_This => + This.Get_Task_Context + (U.Or_Stack.Vector (I).Was_After), + Src => Tc, + Before_This => + This.Get_Task_Context + (U.Or_Stack.Vector (I).Was_Before), + New_Owner => Agent_Id (+U.Or_Stack.Vector (I).Owner_Was)); + + This.Add_Or_Contexts (This.Context.Ref.Plan.Get_Node (Tc.Job)); + + if This.Minsum /= U.Or_Stack.Vector (I).Minsum_Was then + raise Program_Error + with "Undo (Switch) breached integrity; MinSum is" & + This.Minsum'Img & " but should be" & + U.Or_Stack.Vector (I).Minsum_Was'Img; + end if; + + if Expensive_Checks and then not This.Is_Sane then + raise Program_Error; + end if; + end; + end loop; + when others => + raise Program_Error; + end case; + + Log ("UNDONE SWITCH", Debug, Detail_Section); + end Undo_Switch; + function Proper_Cost (This : in Costs) return Costs; pragma Inline (Proper_Cost); + ----------------- + -- Proper_Cost -- + ----------------- + function Proper_Cost (This : in Costs) return Costs is begin if This = Infinite then @@ -2297,14 +2715,17 @@ package body Agpl.Cr.Mutable_Assignment is end if; if Plus_1 = Infinite then + Log ("Plus_1 invalidating ass", Debug, Detail_Section); Plus_1 := Cost_For_Invalid_Task; This.Valid := False; end if; if Plus_2 = Infinite then + Log ("Plus_2 invalidating ass", Debug, Detail_Section); Plus_2 := Cost_For_Invalid_Task; This.Valid := False; end if; if Minus = Infinite then + Log ("Minus invalidating ass", Debug, Detail_Section); Minus := Cost_For_Invalid_Task; This.Valid := False; end if; @@ -2375,14 +2796,17 @@ package body Agpl.Cr.Mutable_Assignment is if Minus_1 = Infinite then + Log ("Minus_1 invalidating ass", Debug, Detail_Section); Minus_1 := Cost_For_Invalid_Task; This.Valid := False; end if; if Minus_2 = Infinite then + Log ("Minus_2 invalidating ass", Debug, Detail_Section); Minus_2 := Cost_For_Invalid_Task; This.Valid := False; end if; if Plus = Infinite then + Log ("Plus invalidating ass", Debug, Detail_Section); Plus := Cost_For_Invalid_Task; This.Valid := False; end if; @@ -2428,4 +2852,30 @@ package body Agpl.Cr.Mutable_Assignment is Log ("Agent: " & (+This.Agent_Name), Always, Log_Section); end Debug_Dump; + function Or_Key (This : in String) return Solution_Context_Key is + begin + return Solution_Context_Key (U ("OR:" & This)); + end Or_Key; + + --------- + -- Key -- + --------- + + function Key (This : in Or_Context) return Solution_Context_Key is + use Htn.Plan_Node; + begin + return Or_Key (Get_Id (This.Node)); + end Key; + + ---------------- + -- Debug_Dump -- + ---------------- + + procedure Debug_Dump (This : in Or_Context) is + use Htn.Plan_Node; + begin + Log ("OR node: " & Get_Id (This.Node) & + "; Branch taken: " & Get_Id (This.Branch), Always, Log_Section); + end Debug_Dump; + end Agpl.Cr.Mutable_Assignment; diff --git a/agpl-cr-mutable_assignment.ads b/agpl-cr-mutable_assignment.ads index c5ce6ea..611eda0 100644 --- a/agpl-cr-mutable_assignment.ads +++ b/agpl-cr-mutable_assignment.ads @@ -34,6 +34,7 @@ with Agpl.Cr.Agent.Containers; with Agpl.Cr.Assignment; with Agpl.Cr.Cost_Matrix; with Agpl.Dynamic_Vector; +with Agpl.Generic_Handle; with Agpl.Htn.Plan; with Agpl.Htn.Tasks; with Agpl.Htn.Tasks.Containers; @@ -62,11 +63,10 @@ package Agpl.Cr.Mutable_Assignment is subtype Agent_Id is String; - type Undo_Info is private; + type Undo_Info (<>) is private; type Mutation_Doer is access procedure (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info); + Undo : out Undo_Info); type Mutation_Undoer is access procedure (This : in out Object; Undo : in Undo_Info); @@ -77,6 +77,11 @@ package Agpl.Cr.Mutable_Assignment is function Evaluate (This : in Object) return Costs; -- Uses the internal criterion + pragma Inline (Evaluate); + + function Evaluate (This : in Object) return Optimization.Cost; + -- For convenience + pragma Inline (Evaluate); function Evaluate (This : in Object; Criterion : in Assignment_Criteria) return Costs; @@ -122,15 +127,17 @@ package Agpl.Cr.Mutable_Assignment is procedure Set_Criterion (This : in out Object; Criterion : in Assignment_Criteria); - procedure Set_Tasks (This : in out Object; - Plan : in Htn.Plan.Object); + procedure Set_Tasks (This : in out Object; + Plan : in Htn.Plan.Object; + Assign : in Boolean := True); -- The tasks are provided in Plan form, inflated or not. -- Warning! The plan *MUST NOT* contain Starting_Pose tasks. -- This is managed internally. -- *HOWEVER* the Costs must contemplate the starting task! - -- No attempt to assignation is made. All dynamic data structures will be + -- All dynamic data structures will be -- cleared. You should call Create_Some_Solution or To_Assignment - -- subsequently. + -- subsequently, unless Assign is true that causes: + -- This.Set_Assignment (This.To_Assignment); procedure Create_Some_Solution (This : in out Object; Criterion : in Assignment_Criteria); @@ -154,7 +161,6 @@ package Agpl.Cr.Mutable_Assignment is -- weights given here. procedure Do_Identity (This : in out Object; - Desc : out Ustring; Undo : out Undo_Info); procedure Undo_Identity (This : in out Object; Undo : in Undo_Info); -- Test mutation, does nothing! @@ -163,76 +169,69 @@ package Agpl.Cr.Mutable_Assignment is -- Undo for heuristics procedure Do_Heuristic_1 (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info); + Undo : out Undo_Info); -- Will consider all agents and tasks to provide some "good" assignment. -- The current tasks are re-assigned in a "best pair" greedy fashion. -- So no OR node switchings happen. procedure Do_Heuristic_2 (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info); + Undo : out Undo_Info); -- This heuristic will consider the best of *all* tasks in every possible -- expansion; freeze the plan with the chosen task; repeat until no more T. -- O (n^2) procedure Do_Agent_Reorder (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info); + Undo : out Undo_Info); -- Greedy reordering of an agent tasks -- O (log) procedure Do_Auction_Task (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info); + Undo : out Undo_Info); -- As undo, use the Undo_Move_Task -- Cost is kept logaritmic checking only a log fraction of all insertion points. procedure Do_Guided_Auction_Task (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info); + Undo : out Undo_Info); -- Guided in originating agent -- As undo, use the Undo_Move_Task -- O (n) procedure Do_Exhaustive_Auction_Task (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info); + Undo : out Undo_Info); -- As undo, use the Undo_Move_Task -- Will try all possible insertions -- O (log) procedure Do_Move_Task (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info); + Undo : out Undo_Info); procedure Undo_Move_Task (This : in out Object; Undo : in Undo_Info); -- Will un-move all movements, in the Undo_Info stack, not just one. -- O (log) procedure Do_Move_Task_Changing_Owner (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info); + Undo : out Undo_Info); -- Moves a task at random, but choses the owner before hand. In this way, -- no agent can end without tasks (as happens when just using Move_Task -- As undo, use the Undo_Move_Task procedure Do_Guided_Move_Task_Changing_Owner (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info); + Undo : out Undo_Info); -- Like previous, but task is chosen from the worst cost agent procedure Do_Swap_Order (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info); + Undo : out Undo_Info); -- Switches two consecutive tasks -- As undo, use the Undo_Move_Task procedure Do_Swap_Tasks (This : in out Object; - Desc : out Ustring; - Undo : in out Undo_Info); + Undo : out Undo_Info); -- Switches two arbitrary tasks -- As undo, use the Undo_Move_Task + procedure Do_Switch_Or_Node (This : in out Object; + Undo : out Undo_Info); + procedure Undo_Switch (This : in out Object; Undo : in Undo_Info); + ----------------- -- CONVERSIONS -- ----------------- @@ -366,7 +365,19 @@ private ---------------- -- OR_Context -- ---------------- - Continue here + type Or_Context is new Solution_Context with record + Node : Htn.Plan.Subplan; + Branch : Htn.Plan.Subplan; + end record; + -- Since the plan is kept unmodified, there is no problem using accesses + -- here. + + function Or_Key (This : in String) return Solution_Context_Key; + pragma Inline (Or_Key); + -- Get a key form a node ID + function Key (This : in Or_Context) return Solution_Context_Key; + pragma Inline (Key); + procedure Debug_Dump (This : in Or_Context); type Minimax_Key is record Cost : Costs; @@ -401,14 +412,34 @@ private package Undo_Move_Vectors is new Agpl.Dynamic_Vector (Undo_Move_Task_Info, 2); - type Undo_Info is record - Ass : Assignment.Object; -- For scratch starting + type Undo_Kinds is (Identity, From_Scratch, Move_Task, Switch_Or_Node); + + type Undo_Internal (Kind : Undo_Kinds) is record + Description : Ustring; + + case Kind is + when Identity => + null; + when From_Scratch => + Ass : Assignment.Object := Assignment.Empty_Object; + -- For scratch starting + when Move_Task => + Move_Stack : Undo_Move_Vectors.Object (First => 1); + -- LIFO stack of moved tasks. To undo, just undo movements from + -- tail to head + when Switch_Or_Node => + Actived_Or_Branch : Htn.Plan.Subplan := null; + -- We will de-activate this branch when undoing + Or_Stack : Undo_Move_Vectors.Object (First => 1); + -- The tasks that were removed must be replaced where they were, + -- activating their respective OR nodes... + end case; + end record; - Move_Stack : Undo_Move_Vectors.Object (First => 1); - -- LIFO stack of moved tasks. To undo, just undo movements from tail to H + package Undo_Handle is new Agpl.Generic_Handle (Undo_Internal); - Was_Valid : Boolean; - -- Says if the previous state was a valid solution. + type Undo_Info is record + Handle : Undo_Handle.Object; end record; procedure Reset (This : in out Undo_Info); @@ -422,7 +453,8 @@ private -- Invariant information Context : Smart_Static_Contexts.Object; - Valid : Boolean; -- Is the assignment valid? + Valid : Boolean := True; + -- Is the assignment valid? Initially empty (hence valid). Contexts : Solution_Context_Maps.Map; -- All contextual infos (tasks, Or-nodes, etc) @@ -437,6 +469,7 @@ private -- All assigned tasks (Task_Context.Key) -- A bag for each agent, containing its tasks (Task_Context.Key) -- A bag with agent contexts (Agent_Context.Key) + -- A bag with active OR nodes (Or_Context_Key) -- The current solution costs MinSum : Costs; @@ -444,10 +477,14 @@ private Agent_Costs : Agent_Cost_Maps.Map; -- Undo information - Last_Mutation_Description : Ustring := +"None"; - Last_Mutation_Index : Positive; - Last_Mutation_Undo : Undo_Info; - Last_Mutation_Exists : Boolean := False; + Was_Valid : Boolean := False; + -- Previous object state. + + Last_Mutation : Ustring; -- Cached copy + + Undo : Undo_Info := (Handle => Undo_Handle.Null_Object); + Undoer : Mutation_Undoer; + -- Info needed to reconstruct object. end record; -- Controlling... @@ -479,7 +516,7 @@ private procedure Add_Undo_Move (This : in Object; Job : in Task_Context_Ptr; - Undo : in out Undo_Info); + Undo : in out Undo_Internal); procedure Do_Insert_Task (This : in out Object; After_This : in Task_Context_Ptr; @@ -542,8 +579,9 @@ private function Task_Key (Id : in Htn.Tasks.Task_Id) return Solution_Context_Key; pragma Inline (Task_Key); - All_Assigned_Tasks : constant Bag_Key := "aat"; - All_Agents : constant Bag_Key := "aag"; + All_Assigned_Tasks : constant Bag_Key := "aat"; + All_Agents : constant Bag_Key := "aag"; + All_Active_Or_Nodes : constant Bag_Key := "aaon"; function Agent_Tasks_Bag (Name : in Agent_Id) return Bag_Key; pragma Inline (Agent_Tasks_Bag); @@ -649,9 +687,26 @@ private overriding procedure Debug_Dump (This : in Task_Context); + -- CONTEXTS -- + procedure Remove_Context (This : in out Object; + Key : in Solution_Context_Key); + -- Remove from contexts list and bags + + procedure Add_Or_Contexts (This : in out Object; + Node : in Htn.Plan.Subplan); + -- Will check if Id parent is an OR context and if so add it. + -- In any case it will climb up until root is reached. + + procedure Descend_Removing (This : in out Object; + Node : in Htn.Plan.Subplan; + Undo : in out Undo_Internal); + -- FUNCTIONS THAT MANIPULATE BAGS AND HAVE TO BE UPDATED ON ADDITION OF -- NEW BAGS: -- Set_Assignment -- Do_Insert_Task + -- Descend_Removing + -- Descend_Adding + -- Add_Or_Nodes end Agpl.Cr.Mutable_Assignment; diff --git a/agpl-gdk/agpl-gdk-drawer.adb b/agpl-gdk/agpl-gdk-drawer.adb index 408204b..b027cc8 100644 --- a/agpl-gdk/agpl-gdk-drawer.adb +++ b/agpl-gdk/agpl-gdk-drawer.adb @@ -162,6 +162,15 @@ package body Agpl.Gdk.Drawer is Clear (This.Queue); end Draw_End; + ------------------ + -- Get_Drawable -- + ------------------ + + function Get_Drawable (This : in Object) return Gdk_Drawable is + begin + return This.Draw; + end Get_Drawable; + --------------- -- Immediate -- --------------- diff --git a/agpl-gdk/agpl-gdk-drawer.ads b/agpl-gdk/agpl-gdk-drawer.ads index c7af62a..d61e65e 100644 --- a/agpl-gdk/agpl-gdk-drawer.ads +++ b/agpl-gdk/agpl-gdk-drawer.ads @@ -54,6 +54,8 @@ package Agpl.Gdk.Drawer is procedure Keep_Aspect_Ratio (This : in out Object; Keep : Boolean := True); -- Defaults to Yes. + function Get_Drawable (This : in Object) return Gdk_Drawable; + procedure Set_Drawable (This : in out Object; D : Gdk_Drawable); -- Where to draw into. Must be set before any actual drawing is attempted. diff --git a/agpl-htn-plan.adb b/agpl-htn-plan.adb index ece1726..576bfe0 100644 --- a/agpl-htn-plan.adb +++ b/agpl-htn-plan.adb @@ -800,6 +800,7 @@ package body Agpl.Htn.Plan is " E:" & Boolean_To_Char (Get_Expanded (X)) & " F:" & Boolean_To_Char (Get_Finished (X)) & " | " & + "[" & Get_Id (X) & "]" & "[" & Strings.To_String (Integer (Htn.Tasks.Get_Id (Job.all))) & "] " & Htn.Tasks.To_String (Job.all) & External_Tag (Job'Tag), diff --git a/agpl-htn-plan_node.ads b/agpl-htn-plan_node.ads index 5f8e704..2fd0587 100644 --- a/agpl-htn-plan_node.ads +++ b/agpl-htn-plan_node.ads @@ -54,6 +54,8 @@ package Agpl.Htn.Plan_Node is type Node_Access is access Object; -- for Node_Access'Storage_Pool use Agpl.Debug.Pool; + subtype Node_Id is String; + function Equivalent (L, R : in Node_Access) return Boolean; -- Compares task bodies and node ids diff --git a/agpl-htn-tasks.adb b/agpl-htn-tasks.adb index 6423cc1..e407a2e 100644 --- a/agpl-htn-tasks.adb +++ b/agpl-htn-tasks.adb @@ -53,17 +53,27 @@ package body Agpl.Htn.Tasks is Free (This); end Delete; + -------------- + -- Force_Id -- + -------------- + + procedure Force_Id (This : in out Object; Id : in Task_Id) is + begin + This.Id := Id; + end Force_Id; + ------------ -- Get_Id -- ------------ function Get_Id (This : in Object) return Task_Id is begin - if This.Id = 0 then - raise Id_Error; - else - return This.Id; - end if; + return This.Id; +-- if This.Id = 0 then +-- raise Id_Error; +-- else +-- return This.Id; +-- end if; end Get_Id; ------------------ diff --git a/agpl-htn-tasks.ads b/agpl-htn-tasks.ads index ff330a2..31681e1 100644 --- a/agpl-htn-tasks.ads +++ b/agpl-htn-tasks.ads @@ -105,6 +105,9 @@ package Agpl.Htn.Tasks is function Same_Id (L, R : in Object'Class) return Boolean; -- Equality by id. + procedure Force_Id (This : in out Object; Id : in Task_Id); + -- Forcefully assign an Id to a task + private No_Task : constant Task_Id := 0; diff --git a/agpl-optimization-annealing-solver.adb b/agpl-optimization-annealing-solver.adb index 410a223..cb587d7 100644 --- a/agpl-optimization-annealing-solver.adb +++ b/agpl-optimization-annealing-solver.adb @@ -132,6 +132,13 @@ package body Agpl.Optimization.Annealing.Solver is -- Log ("Move: " & Last_Mutation (New_Sol), Always); This.Iterations := This.Iterations + 1; + Log ("[NC/OC/Rnd/Goodnes]:" & + Image (New_Cost) & "/" & + Image (This.Curr_Cost) & "/" & + Image (Cost (P)) & "/" & + Image (Cost (Goodness)), + Debug, Detail_Section); + if New_Cost = Infinite then -- Invalid solution This.Wasted := This.Wasted + 1; This.Add_Move @@ -195,24 +202,24 @@ package body Agpl.Optimization.Annealing.Solver is To_String (Float (M.Accepted) * 100.0 / Float (Total_Good)) & "%/" & To_String (Float (M.Taken) * 100.0 / Float (Total_Moves)) & "%", - Debug, Log_Section); + Always, Log_Section); end Do_Inform; begin Stats.Iterate (Do_Count'Access); - Log ("", Debug, Log_Section); + Log ("", Always, Log_Section); Stats.Iterate (Do_Inform'Access); - Log ("", Debug, Log_Section); + Log ("", Always, Log_Section); Log ("TOTAL MOVES (accept/total): " & To_String (Total_Good) & "/" & To_String (Total_Moves) & " (" & To_String (Float (Total_Good) * 100.0 / Float (Total_Moves)) & "%)", - Debug, Log_Section); + Always, Log_Section); - Log ("", Debug, Log_Section); + Log ("", Always, Log_Section); end Print_Stats; ----------------- @@ -224,6 +231,15 @@ package body Agpl.Optimization.Annealing.Solver is Print_Stats (This.Stats); end Print_Stats; + ----------------- + -- Reset_Stats -- + ----------------- + + procedure Reset_Stats (This : in out Object) is + begin + This.Stats.Clear; + end Reset_Stats; + -------------------------- -- Set_Initial_Solution -- -------------------------- diff --git a/agpl-optimization-annealing-solver.ads b/agpl-optimization-annealing-solver.ads index a69df65..e5a73e6 100644 --- a/agpl-optimization-annealing-solver.ads +++ b/agpl-optimization-annealing-solver.ads @@ -120,6 +120,7 @@ package Agpl.Optimization.Annealing.Solver is -- This allows "chunking" the computation procedure Print_Stats (This : in Object); + procedure Reset_Stats (This : in out Object); private diff --git a/agpl-optimization-annealing.adb b/agpl-optimization-annealing.adb index f790692..b82169d 100644 --- a/agpl-optimization-annealing.adb +++ b/agpl-optimization-annealing.adb @@ -24,6 +24,9 @@ -- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -- ------------------------------------------------------------------------------ +with Agpl.Chronos; +with Agpl.Trace; use Agpl.Trace; + with Ada.Numerics.Elementary_Functions; package body Agpl.Optimization.Annealing is @@ -101,11 +104,58 @@ package body Agpl.Optimization.Annealing is Local_T := Top; end Reset; + ------------ + -- Divide -- + ------------ + procedure Divide (Denom : in Float := 2.0) is begin Local_T := Temperature (Float (Local_T) / Denom); end Divide; + ------------ + -- Update -- + ------------ + + Cool_Timer : Chronos.Object; + Settle_Timer : Chronos.Object; + Prev_C : Cost := Cost'Last; + Local_Min : Cost := Cost'Last; + use Chronos; + + procedure Update (Current_Cost : in Cost) is + begin + if Current_Cost < Prev_C then + Settle_Timer.Reset; + Log ("Reseting Settle " & Image (Current_Cost) & " " & + Image (Prev_C), Debug, Detail_Section); + end if; + if Current_Cost < Local_Min then + Local_Min := Current_Cost; + Cool_Timer.Reset; + Log ("Reseting Cooling " & Image (Current_Cost) & " " & + Image (Local_Min), Debug, Detail_Section); + end if; + + if Elapsed (Cool_Timer) > Cool_Time then + Divide (Divisor); + Cool_Timer.Reset; + Log ("Cooling...", Debug, Detail_Section); + end if; + + if -- Local_T <= Settle_Umbral and then + Elapsed (Settle_Timer) > Settle_Time + then + Local_T := Ceiling_Temperature; + Local_Min := Cost'Last; + Settle_Timer.Reset; + Cool_Timer.Reset; + Log ("Temperature bump!", Debug, Detail_Section); + end if; + + Prev_C := Current_Cost; + end Update; + end Manual_Cooling; end Agpl.Optimization.Annealing; diff --git a/agpl-optimization-annealing.ads b/agpl-optimization-annealing.ads index 1de180f..3010b76 100644 --- a/agpl-optimization-annealing.ads +++ b/agpl-optimization-annealing.ads @@ -68,11 +68,27 @@ package Agpl.Optimization.Annealing is -- Note that Start is reset if Clock - Start > Period generic - Initial_Temperature : Temperature := 1.0; + Initial_Temperature : Temperature := 0.0; + Ceiling_Temperature : Temperature := 0.002; + -- When auto re-heating, go to this temperature + + Settle_Umbral : Temperature := Temperature'Small * 2.0; + -- When checking for no progress, this is the "absolute zero"; + -- if not reached, no check. + + Cool_Time : Duration := 0.5; + -- Time without progress that will cause cooling. + + Settle_Time : Duration := 10.0; + -- Time without progress under Settle_Umbral until re-heating + + Divisor : Float := 1.85; + -- The amount to divide temperature if no progress package Manual_Cooling is -- Expected package usage is to manually divide temperature when you need -- it. -- In this way you can keep low temperatures for as long as necessary. + -- Or, using Update, it will be done for you function Get_Temperature (T : in Temperature) return Temperature; pragma Inline (Get_Temperature); @@ -83,6 +99,9 @@ package Agpl.Optimization.Annealing is procedure Divide (Denom : in Float := 2.0); -- Divide temperature by the given Denominator + + procedure Update (Current_Cost : in Cost); + -- To use auto updating end Manual_Cooling; end Agpl.Optimization.Annealing; diff --git a/agpl-optimization.adb b/agpl-optimization.adb new file mode 100644 index 0000000..727bbee --- /dev/null +++ b/agpl-optimization.adb @@ -0,0 +1,16 @@ +with Agpl.Conversions; + +package body Agpl.Optimization is + + function S is new Conversions.To_Str (Cost); + + ----------- + -- Image -- + ----------- + + function Image (C : in Cost; Decimals : in Natural := 2) return String is + begin + return S (C, Decimals); + end Image; + +end Agpl.Optimization; diff --git a/agpl-optimization.ads b/agpl-optimization.ads index b4053a4..6623d9e 100644 --- a/agpl-optimization.ads +++ b/agpl-optimization.ads @@ -26,7 +26,7 @@ package Agpl.Optimization is - pragma Pure; + pragma Preelaborate; -- type Cost is delta 0.00001 digits 18; -- type Cost is new Long_Float; @@ -34,6 +34,8 @@ package Agpl.Optimization is -- We are *minimizing* the solution cost. -- If you want ot maximize an utility function, simply negate your function. + function Image (C : in Cost; Decimals : in Natural := 2) return String; + Infinite : constant Cost := Cost'Last; end Agpl.Optimization;