Icon representing a recipe

Recipe: Random Pull and Jiggle 2.1

created by RichGuilmain

Profile


Name
Random Pull and Jiggle 2.1
ID
108487
Shared with
Public
Parent
Random Pull and Jiggle 2.0
Children
None
Created on
November 15, 2023 at 02:19 AM UTC
Updated on
November 15, 2023 at 22:13 PM UTC
Description

Randomly pulls the protein around and then wiggles it back together to escape local optima and find better neighboring configurations until canceled.

Best for


Code


--[[ Random Pull and Jiggle by RichGuilmain Version 2.1 + Only attaches bands to unlocked segments + Only uses maximum wiggle power if specified via new GUI checkbox + Updates default parameter settings ]]-- -- Setup math.randomseed(os.time()) num_segments = structure.GetCount() starting_wiggle_power = behavior.GetWigglePower() starting_score = current.GetScore() unlocked_segments = {} for i=1, num_segments do if structure.IsLocked(i) == false then table.insert(unlocked_segments, i) end end -- Parameters min_bands = 1 max_bands = 5 min_band_dist = 0.0 max_band_dist = 5.0 min_band_goal = 0.0 max_band_goal = 0.0 min_band_str = 1.0 max_band_str = 1.0 min_wiggles_out = 1 max_wiggles_out = 3 max_wiggles_in = 25 mutate_before = 0 shake_before = 0 mutate_after = 0 shake_after = 0 use_max_wiggle_power = false function Round(n) return math.floor(n * 1000 + 0.5) / 1000 end function RandFloat(min, max) return min + (max - min) * math.random() end function AddRandomBand() local origin = unlocked_segments[math.random(#unlocked_segments)] local x_axis = math.random(num_segments) local y_axis = math.random(num_segments) local rho = RandFloat(min_band_dist, max_band_dist) local theta = math.rad(180 * math.random()) local phi = math.rad(360 * math.random()) if not pcall(band.Add, origin, x_axis, y_axis, rho, theta, phi) then origin = AddRandomBand() end return origin end function WiggleOut() -- Attach random bands to random segments for i=1, math.random(min_bands, max_bands) do selection.Select(AddRandomBand()) band.SetStrength(i, RandFloat(min_band_str, max_band_str)) band.SetGoalLength(i, RandFloat(min_band_goal, max_band_goal)) end -- Wiggle out, and save any better solutions we find along the way local wiggle_out_improved_score = false for i=1, math.random(min_wiggles_out, max_wiggles_out) do structure.WiggleAll(1) if current.GetScore() > recentbest.GetScore() then recentbest.Save() wiggle_out_improved_score = true end end -- Use the best intermediary configuration if any scored better than the baseline if wiggle_out_improved_score then recentbest.Restore() end band.DeleteAll() end function WiggleBack() -- Returns true if wiggling back scores better than the recent best baseline local recent_best_score = recentbest.GetScore() local function_start_score = current.GetScore() for i=1, max_wiggles_in do -- Measure the improvement from one wiggle local loop_start_score = current.GetScore() structure.WiggleAll(1) local loop_end_score = current.GetScore() -- Break if we stop making progress if (loop_end_score - loop_start_score < 0.001) then break end -- Break if it looks like we won't make it back to our baseline score in time local remaining_gain = recent_best_score - loop_end_score local average_loop_gain = (loop_end_score - function_start_score) / i local remaining_wiggles = max_wiggles_in - i if (remaining_gain > average_loop_gain * remaining_wiggles) then break end end return current.GetScore() > recent_best_score end function Mutate(level) if level == 1 then structure.MutateSidechainsSelected(1) end if level == 2 then structure.MutateSidechainsAll(1) end end function Shake(level) if level == 1 then structure.ShakeSidechainsSelected(1) end if level == 2 then structure.ShakeSidechainsAll(1) end end function PullAndJiggle() WiggleOut() -- Mutate/shake before wiggling back together Mutate(mutate_before) Shake(shake_before) local wiggle_back_improved_score = WiggleBack() -- Mutate/shake after wiggling back together if wiggle_back_improved_score then Mutate(mutate_after) Shake(shake_after) end -- Clean up and save solution if we improved over the baseline selection.DeselectAll() if current.GetScore() > recentbest.GetScore() then recentbest.Save() end end function MonotonicPullAndJiggle() local starting_score = current.GetScore() PullAndJiggle() recentbest.Restore() local ending_score = current.GetScore() local score_delta = Round(ending_score - starting_score) if score_delta > 0.0 then print(os.date("%X"), "Increased score by", score_delta, "to", Round(ending_score)) end end function MainDialog() local args = dialog.CreateDialog("Random Pull and Jiggle 2.1") args.doc1 = dialog.AddLabel("Randomly pulls the protein around and then wiggles") args.doc2 = dialog.AddLabel("it back together to escape local optima and find") args.doc3 = dialog.AddLabel("better neighboring configurations until canceled.") args.doc4 = dialog.AddLabel("Number of bands:") args.min_bands = dialog.AddSlider("min", min_bands, 1, 10, 0) args.max_bands = dialog.AddSlider("max", max_bands, 1, 10, 0) args.doc5 = dialog.AddLabel("Band lengths:") args.min_band_dist = dialog.AddSlider("min", min_band_dist, 0.0, 20.0, 1) args.max_band_dist = dialog.AddSlider("max", max_band_dist, 0.0, 20.0, 1) args.doc6 = dialog.AddLabel("Number of wiggles in:") args.max_wiggles_in = dialog.AddSlider("max", max_wiggles_in, 1, 100, 0) args.doc7 = dialog.AddLabel("Amount to mutate/shake before/after wiggling in:") args.doc8 = dialog.AddLabel("Amounts: 0 = none, 1 = selected, 2 = all (slower)") args.mutate_before = dialog.AddSlider("Mutate before", mutate_before, 0, 2, 0) args.shake_before = dialog.AddSlider("Shake before", shake_before, 0, 2, 0) args.doc9 = dialog.AddLabel("Only runs if wiggling back in improves baseline:") args.mutate_after = dialog.AddSlider("Mutate after", mutate_after, 0, 2, 0) args.shake_after = dialog.AddSlider("Shake after", shake_after, 0, 2, 0) args.use_max_wiggle_power = dialog.AddCheckbox("Use max wiggle power", use_max_wiggle_power) args.ok = dialog.AddButton("OK", 1) args.more = dialog.AddButton("More", 2) args.cancel = dialog.AddButton("Cancel", 0) return_code = dialog.Show(args) if return_code > 0 then min_bands = args.min_bands.value max_bands = args.max_bands.value min_band_dist = args.min_band_dist.value max_band_dist = args.max_band_dist.value max_wiggles_in = args.max_wiggles_in.value mutate_before = args.mutate_before.value shake_before = args.shake_before.value mutate_after = args.mutate_after.value shake_after = args.shake_after.value use_max_wiggle_power = args.use_max_wiggle_power.value end return return_code end function SecondaryDialog() local args = dialog.CreateDialog("Random Pull and Jiggle 2.1") args.doc1 = dialog.AddLabel("Band goal lengths:") args.min_band_goal = dialog.AddSlider("min", min_band_goal, 0.0, 20.0, 1) args.max_band_goal = dialog.AddSlider("max", max_band_goal, 0.0, 20.0, 1) args.doc2 = dialog.AddLabel("Band strengths:") args.min_band_str = dialog.AddSlider("min", min_band_str, 0.0, 20.0, 1) args.max_band_str = dialog.AddSlider("max", max_band_str, 0.0, 20.0, 1) args.doc3 = dialog.AddLabel("Number of wiggles out:") args.min_wiggles_out = dialog.AddSlider("min", min_wiggles_out, 1, 10, 0) args.max_wiggles_out = dialog.AddSlider("max", max_wiggles_out, 1, 10, 0) args.ok = dialog.AddButton("OK", 1) args.cancel = dialog.AddButton("Cancel", 0) return_code = dialog.Show(args) if return_code > 0 then min_band_goal = args.min_band_goal.value max_band_goal = args.max_band_goal.value min_band_str = args.min_band_str.value max_band_str = args.max_band_str.value min_wiggles_out = args.min_wiggles_out.value max_wiggles_out = args.max_wiggles_out.value end return return_code end function GetParams() repeat main_dialog_return_code = MainDialog() if main_dialog_return_code == 2 then SecondaryDialog() end until main_dialog_return_code < 2 return main_dialog_return_code == 1 end function ValidateParams() if min_bands > max_bands then print("Bad parameters. Min bands must be less than max bands.") return false end if min_band_dist > max_band_dist then print("Bad parameters. Min band length must be less than max band length.") return false end if min_band_goal > max_band_goal then print("Bad parameters. Min band goal length must be less than max band goal length.") return false end if min_band_str > max_band_str then print("Bad parameters. Min band strength out must be less than max band strength.") return false end if min_wiggles_out > max_wiggles_out then print("Bad parameters. Min wiggles out must be less than max wiggles out.") return false end return true end function LevelName(level) level_names = {"no", "selected", "all"} return level_names[level+1] end function SetWigglePower() if use_max_wiggle_power then if behavior.HighPowerAllowed() then behavior.SetWigglePower("h") else behavior.SetWigglePower("m") end end end function Main() -- Get parameters if not GetParams() or not ValidateParams() then return end -- Initialize SetWigglePower() band.DeleteAll() selection.DeselectAll() recentbest.Save() -- Log settings to the console print(os.date("%X"), "Random Pull and Jiggle 2.1") print(os.date("%X"), "########## SETTINGS ##########") print(os.date("%X"), "Num bands:", "min =", min_bands, "max =", max_bands) print(os.date("%X"), "Band lengths:", "min =", min_band_dist, "max =", max_band_dist) print(os.date("%X"), "Band goal lengths:", "min =", min_band_goal, "max =", max_band_goal) print(os.date("%X"), "Band strengths:", "min =", min_band_str, "max =", max_band_str) print(os.date("%X"), "Wiggles out:", "min =", min_wiggles_out, "max =", max_wiggles_out) print(os.date("%X"), "Wiggles in:", "min =", 1, "max =", max_wiggles_in) print(os.date("%X"), "Before:", "mutate =", LevelName(mutate_before), "shake =", LevelName(shake_before)) print(os.date("%X"), "After:", "mutate =", LevelName(mutate_after), "shake =", LevelName(shake_after)) print(os.date("%X"), "Wiggle power:", behavior.GetWigglePower()) print(os.date("%X"), "##### PULLING AND JIGGLING... #####") print(os.date("%X"), "Starting with a score of", Round(starting_score)) -- Run while true do MonotonicPullAndJiggle() end end function Cleanup() if current.GetScore() < recentbest.GetScore() then recentbest.Restore() end band.DeleteAll() selection.DeselectAll() behavior.SetWigglePower(starting_wiggle_power) local ending_score = current.GetScore() print(os.date("%X"), "Total score increase of", Round(ending_score - starting_score)) print(os.date("%X"), "Finished with a score of", Round(ending_score)) end xpcall(Main, Cleanup)

Comments