Profile
- Name
- Simulated Annealing 3.0
- ID
- 100951
- Shared with
- Public
- Parent
- None
- Children
- Created on
- May 03, 2015 at 22:25 PM UTC
- Updated on
- May 03, 2015 at 22:25 PM UTC
- Description
various upgrades
Best for
Code
--------------------------------------------------------------------
-- Code for Simulated Annealing algorithm
--------------------------------------------------------------------
-- Performs "modifications" of a protein and accepts or rejects them depending on "temperature"
-- and on how much they improved the score (if + improvement, yes; if not too bad score drop
-- and temperature is "high", then maybe yes). Temperature starts high, but drops gradually to 0
-- by end of run.
--
-- On Win7 box with ~50 AAs, a 100 step run takes 30 min - 1 hour; with ~100 AAs, ~90 min
-- Plan runs accordingly.
--
-- TODO:
-- 1. Cysteine: Include band-two-cysteines together (locate closest ones, match together, etc).
-- Consider throwing out actions that break connections.
-- 2. Gui: Upgrade advanced options so users can directly choose more things (especially in MarchWiggle).
-- 3. Wigglers: Play with banded wiggles - compare them, try adjusting target deltas
-- depending on "lateness" in folding.
-- Compare qStabWiggler with and without the fuse action - time vs score
-- Study cementing behavior wrt fusing and LocalWiggle.
-- 4. SA-Temperature functions: Study effects of different choices of these (probably depending on lateness).
-- 5. Rebuild: Review all my rebuilders, see if there's a way to clean them up.
-- Bring secondary structure into play better. Rebuild with compressor bands.
-- 6. Idealize: Compare with cuts versus without, see if lengths should vary. Maybe put a compressor instead
-- of zlb?
-- 7. Contactmaps: Try more actions such as "put band(s)" + rebuild at low ci; use ZLB-wiggler. "Til contact is
-- made" banded wiggler. TEST comp-band mods: are they working propery?
-- 8. See if the LowCI stuff can take the place of my other scripts, or should follow, or what?
--
-- Code ideas and/or thefts come from:
-- (tlaloc) tlaloc Random Tug 4.00
-- (spvincent) Helix Twister 1.0, Loop rebuild 5.0 (NC), Local Quake 1.0, Quaking Rebuild V2 1.0
-- (susume2) Cut and Wiggle Everything v0.1
-- (MurloW) Shock v0.1, Fracture v1.6
-- (MurloW, drjr) Idealize by 3+
-- (porkythepundit) NC Kichen KaboodlEnz 14, Porky's Cement Crusher 1.0 (and ideas for fuses and banded worm)
-- (rav3n_pl) Rav3n_pl GAB BiS v2.0.1 w30 [NC], Rav3n_pl Voids Killer v0.5 NC,
-- Rav3n_pl Push v3, LS Quake v1.1, Worm LWS v2
-- (StackOverflow) Freeze Burst v1.0
-- (tlaloc, rav3n_pl, Seagat2011, thom001) ST - Glycine Hinge REPOST
-- (drjr) drjr - RandomizeR 1.20, Idealize by 3
-- (BitSpawn) general "how to do low CI" (any failures to do right are mine, not his)
--
--------------------------------------------------------------------------------
-- some oddballs that users might want to set
USENORMALSCORE = true -- exploration puzzles would set false
DEBUGRUN = true -- mostly goes with DebugPrint, but could get more use later
SHOW_STATS_REPORT = true
-- what to do with user state (bands, cuts) when running, and what cleanup is best at end
PRESERVE_USER_BANDS = true
PRESERVE_USER_CUTS = false
RESTORE_BEST_AT_END = true
PERFORM_CLEANUP_AT_END = true
KILLING_FILTERS = false
-- simple bander options
COREPULLING_CI = 0.70
BAND_STRENGTH_DEFAULT = 1.0 -- not really meaningful; wigglers strength-modify
BAND_STRENGTH_REGION = 1.0
BAND_MINCOMPRESS = 0.75
BAND_MAXEXPAND = 1.25
BANDEDWIGGLE_TRYCT = 5
BANDEDWIGGLE_TARGET = 500.0
BIS_LENGTH = 5.0 -- max length of a BIS
BAND_TO_SIDECHAINS = true
BAND_ADD_RANDOMS = true
RANDOMBAND_COUNT = 3 -- when random bands are added, this is a plausible count
-- default time to use in shaking
SHAKESIDECHAIN_ITERS = 1
-- values for controlling in-step wiggle timing and gain
QUICKWIGGLE_ITERS = 2
QUICKWIGGLE_GAIN = 0.50 -- for iterative wiggles that happen in quickwiggle time
PROB_QSTAB_EXTRA = 0.50
PROB_LOCALWIGGLE = 0.50 -- see what happens, anyway
-- values for controlling behavior of slow wiggle that's done after an acceptable neighbor is found
USE_SLOWWIGGLE = false -- if false, skip touch-up fuse
SLOWWIGGLE_ITERS = 2
SLOWWIGGLE_GAIN = 0.10
SLOWWIGGLE_THRESHOLD = 50.0
PROB_SLOWWIGGLE_CUTFUSE = 0.75
PROB_SLOWWIGGLE_LOCAL = 0.00 -- for now, we're just going to kill this one
PROB_SLOWWIGGLE_BLUEFUSE = 0.25
-- values for controlling how mutations are handled
ALLOW_SPOT_MUTATION = false -- this is for performing local "spot" changes
MUTATE_NOT_SHAKE = false -- this is for choosing between shake and mutate
NOMUTATELIST = {} -- aa-numbers to not mutate. currently only manually settable (right here)
-- values associated with idealize
IDEALIZE_USE_CUTS = true
IDEALIZE_FORBID_UNHEALED_CUTS = true
IDEALIZE_MAXTRIES = 5 -- a "try" fails if cuts not healed. increase ZLBstrength, try again.
IDEALIZE_ZLB_STRENGTH = 2.0
IDEALIZE_TUBE_RADIUS = 4.0
IDEALIZE_MAX_SEGLEN = 5
-- values associated with rebuilding
REBUILD_FAILSBEFOREQUIT = 75
REBUILD_TRYCOUNT = 5
REBUILD_ADDIDEALIZE = true
REBUILD_USECUTS = true
REBUILD_MAX_SEGLEN = 9
REBUILD_MAX_EARLY_SCOREDROP = 3000.0
REBUILD_MAX_MEDIUM_SCOREDROP = 500.0
REBUILD_MAX_LATE_SCOREDROP = 100.0
-- Clash Importance
SCALE_MAXCI = 1.0
-- values for contact map reading
CONTACTMAP_THRESHOLD = 0.96
CONTACTMAP_TAKEDROPS = false -- allow drops if they improve contact map
CONTACTMAP_MAX_DROP = 25.0
CONTACTMAP_CONTACTING_GOAL = 0.90
CONTACTMAP_NONCONTACTING_GOAL = 0.70
CONTACTMAP_CONTACTING_STRENGTH = 0.50
CONTACTMAP_NONCONTACTING_STRENGTH = 1.50
-- atoms in an amino acid
BETA_CARBON = 5
TERMINAL_BETA = 6
CENTER_CARBON = 2 --this is the default atom for bands to attach to
-------------------------------------------------------------------------------------------------------------
------------ VALUES USERS SHOULD NOT BE TOUCHING ---------------------------------
-------------------------------------------------------------------------------------------------------------
-------------- CONSTANTS THAT PROBABLY SHOULD NEVER CHANGE --------------
REBUILD_INNER_TUBE_RADIUS = 12.0 -- 9.0 has been seen, 7 or 8 recommended for contact map
REBUILD_OUTER_TUBE_RADIUS = 18.0
REBUILD_CM_INNER_TUBE_RADIUS = 8.0
REBUILD_CM_OUTER_TUBE_RADIUS = 16.0
HELIX_DISTANCE_SKIP4 = 6.3 -- for skips of 4
HELIX_DISTANCE_SKIP5 = 8.6 -- for skips of 5
-- QUICKSAVE slots used herein (QS_Run equivalent is taken by QS_TopLevelTemp)
QS_Start = 1
QS_CmBest = 2
QS_Best = 3
QS_ShortUseTemp1 = 94 -- used by low-level subroutines that stash state for short time
QS_ShortUseTemp2 = 95 -- used by low-level subroutines that stash state for short time
QS_NeighborTemp1 = 96 -- used by "Neighbor Generation" subroutines (mid-level managers)
QS_NeighborTemp2 = 97 -- used by "Neighbor Generation" subroutines (mid-level managers)
QS_NeighborTemp3 = 98 -- used by "Neighbor Generation" subroutines (mid-level managers)
QS_TopLevelTemp = 99 -- used by top-level manager subroutines that call low-level managers
--------------- THINGS THE PROGRAM LEARNS AND REMEMBERS --------------
PuzzleAllowsMutate = false
PuzzleHasLockedSegs = false
PuzzleHasContactMap = false
PuzzleUsesFilters = false
PuzzleIsDesigner = false
ChangedSecondaryStructure = false
ChangedFrozenness = false
EndCalled = false
StartTime = 0
InitialScore = 0.0
CurrentBestScore = 0.0
ContactMapScore = 0.0
InitialBandCount = band.GetCount( )
InitialClashImportance = behavior.GetClashImportance()
HasContactMapSegList = {}
InitialFrozenTable = {}
IsSlowFilterDisabled = behavior.GetSlowFiltersDisabled( )
helixStarts = {}
helixEnds = {}
sheetStarts = {}
sheetEnds = {}
loopStarts = {}
loopEnds = {}
NonLockedSegList = {}
SecStructList = {}
segCt = structure.GetCount()
MinUnlockedSeg = 1
MaxUnlockedSeg = segCt
---------------- STATISTICS-TRACKING ---------------------
algName = "none" -- for tracking statistics about how successful an operation is
algNameList = {}
algStats = {
name = "none",
posScoreDelta = 0.0,
scoreDelta = 0.0,
runTime = 0,
callCount = 0,
failCount = 0,
}
TotalShakeTime = 0
----------------------------------------------------------------------
--
---- SIMULATED-ANNEALING-SPECIFIC STUFF
--
----------------------------------------------------------------------
OperationChosen = "none"
-- Simulated Annealing "core" values
SA_STEPSPERRUN = 100.0
SA_NUMSTARTS = 4
SA_MINGAIN = 5.0
SA_FAILIFNOGAIN = math.floor( SA_STEPSPERRUN / 2 )
SA_REBUILD_NUMSTARTS = 8
-- values for choosing the "scale" of modification to try in SA-Midgame
PROB_TARGETED = 0.00 -- local or midsize action, pointed at "worst-scoring" segment(s)
PROB_LOCAL = 0.15
PROB_MIDSIZE = 0.70
PROB_LARGESCALE = 0.15
PROB_MUTATESIMILAR = 0.75
-- values for controlling final "polishing" wiggles and cleaning
MF_STEPSPERRUN = 30.0
MF_FAILSBEFORERESTART = 15
MARCHWIGGLE_MINGAIN = 15.0 -- if wiggles gain this much, try other actions for luck
FINALWIGGLE_ITERS = 8
FINALRUN_WIGGLE_GAIN = 0.001 -- for final "drag out points" activities
PROB_PUTBAND = 1.0 -- how often do we put bands in banded-worm wiggle steps?
PROB_BIS_NOT_BETWEEN = 0.50 -- do we prefer BIS or bands between segs in banded-worm wiggle
PROB_BETWEEN_USES_TIP = 0.50 -- between: should wiggled seg have band from aa tip for banded-worm
PROB_CHOOSE_CONTACTMAP = 0.80 -- or some other way of saying we like these bands in banded-worm
-- user dialog picker...
DEFAULT_PICK_AT_START = true
----------------------------------------------------------------------
-- ROUTINES FOR CHOOSING ACTIONS
----------------------------------------------------------------------
function PickActionFromList( allowedActions, idx )
local doVariableStrength = false
local skipWiggler = false
local okToAddRandomBands = false
algName = ChooseAlgorithm( allowedActions )
if algName == "BIS" then
changeSucceeded = PickSingleBandInSpace( idx )
skipWiggler = true
elseif algName == "BandBetween" then
changeSucceeded = PickBandToRandomSeg( idx )
doVariableStrength = true
elseif algName == "SpotMutator" then
changeSucceeded = PickMutatorAlgorithm( idx )
elseif algName == "GlycineHinge" then
changeSucceeded = PickGlycineHinge( idx )
elseif algName == "RotatorBands" then
changeSucceeded = PickRotatorBands( idx )
skipWiggler = true
elseif algName == "PusherBands" then
changeSucceeded = PickPusherBands( idx )
skipWiggler = true
elseif algName == "MoverBands" then
changeSucceeded = PickMoverBands( idx )
skipWiggler = true
elseif algName == "FlexerBands" then
changeSucceeded = PickFlexerBands( idx )
skipWiggler = true
elseif algName == "GeneralHinge" then
changeSucceeded = PickGeneralHingeBands( idx )
elseif algName == "SheetStraightener" then
changeSucceeded = PickSheetStraightenerBands( idx )
elseif algName == "HelixUniform" then
changeSucceeded = PickHelixUniformBands( idx )
elseif algName == "HelixCompressor" then
changeSucceeded = PickHelixCompressorBands( idx )
elseif algName == "HelixFixer" then
changeSucceeded = PickHelixFixerBands( idx )
elseif algName == "PushPull" then
changeSucceeded = PickPushPullAction( idx )
doVariableStrength = true
elseif algName == "VoidCrusher" then
changeSucceeded = PickVoidCrusherAction( idx )
doVariableStrength = true
elseif algName == "IdealizePoint" then
changeSucceeded = PickIdealizePointAction( idx )
skipWiggler = true
elseif algName == "IdealizeInRange" then
changeSucceeded = PickIdealizeRangeAction( idx )
skipWiggler = true
elseif algName == "IdealizeSimpleRange" then
changeSucceeded = PickSimpleIdealizeAction( idx )
skipWiggler = true
elseif algName == "RebuildInRange" then
changeSucceeded = PickRebuildPointAction( idx )
skipWiggler = true
elseif algName == "RebuildSimple" then
changeSucceeded = PickRebuildSimpleAction( idx )
skipWiggler = true
elseif algName == "RebuildSimple2" then
changeSucceeded = PickRebuildSimple2Action( idx )
skipWiggler = true
elseif algName == "RebuildSimpleRange" then
changeSucceeded = PickSimpleRebuildRangeAction( idx )
skipWiggler = true
elseif algName == "MultiBander" then
changeSucceeded = PickMultiBander( )
doVariableStrength = true
elseif algName == "CentralBander" then
changeSucceeded = PickCentralBander( )
doVariableStrength = true
elseif algName =="EveryXToY" then
changeSucceeded = PickEveryXToEveryYBanding( )
doVariableStrength = true
elseif algName == "GentleComp" then
changeSucceeded = PickGentleCompBander( )
doVariableStrength = true
elseif algName == "CoreStirrer" then
changeSucceeded = PickCoreStirrer( )
skipWiggler = true
elseif algName == "SheetSewer" then
changeSucceeded = PickSheetSewerAction( )
doVariableStrength = true
elseif algName == "LSQuaker" then
changeSucceeded = PickLSQuaker( )
doVariableStrength = true
elseif algName == "LocalQuake" then
changeSucceeded = PickLocalQuake( idx )
doVariableStrength = true
elseif algName == "ContactMapper" then
changeSucceeded = PickContactMapper( )
okToAddRandomBands = BAND_ADD_RANDOMS
elseif algName == "ContactMapTwoRegions" then
changeSucceeded = PickCMTwoRegionBander( idx )
okToAddRandomBands = BAND_ADD_RANDOMS
elseif algName == "ContactTwoSegs" then
changeSucceeded = PickSingleContactMapBand( idx )
doVariableStrength = true
okToAddRandomBands = BAND_ADD_RANDOMS
elseif algName == "SomeContactMapBands" then
changeSucceeded = PickSomeContactMapBands( )
doVariableStrength = true
okToAddRandomBands = BAND_ADD_RANDOMS
elseif algName == "ContactMapForSeg" then
changeSucceeded = PickContactMapBandsForSeg( idx )
okToAddRandomBands = BAND_ADD_RANDOMS
elseif algName == "PushPullContactMap" then
changeSucceeded = PickPushPullContactMapAction( idx )
doVariableStrength = true
elseif algName == "VoidCrusherContactMap" then
changeSucceeded = PickVoidCrusherContactMapAction( idx )
doVariableStrength = true
elseif algName == "ContactMapRebuildRange" then
changeSucceeded = PickContactMapRebuildRangeAction( idx )
skipWiggler = true
elseif algName == "ContactMapRebuildSS" then
changeSucceeded = PickContactMapRebuildSSAction( idx )
skipWiggler = true
elseif algName == "ContactMapRebuildAll" then
changeSucceeded = PickContactMapRebuildAllAction( )
skipWiggler = true
else
print( "Error - invalid action: ".. algName )
exit( )
end
if changeSucceeded and okToAddRandomBands then
if random( ) < 0.50 then -- consider making a global to control this decision
local ctRandBands = random( RANDOMBAND_COUNT - 2, RANDOMBAND_COUNT + 2 )
local strength = random(0.5, 1.5) * BAND_STRENGTH_DEFAULT
PutGentleCompBands( ctRandBands, 14.0, 30.0, 5, strength, 0.7 ) -- 0.7 is compression
end
end
if changeSucceeded and not skipWiggler then
PickBeforeAndAfterWiggler( doVariableStrength )
end
ResetFrozenness( )
DelBands( )
ResetCuts( )
selection.DeselectAll( )
if not changeSucceeded then print( "CHANGE FAILED" ) end
return changeSucceeded
end
function AddRegionBanderActions(allowedActions, idx )
allowedActions[ #allowedActions + 1 ] = "RotatorBands"
allowedActions[ #allowedActions + 1 ] = "PusherBands"
allowedActions[ #allowedActions + 1 ] = "MoverBands"
local startSeg, endSeg = GetRegionForOperation( idx )
local regionLength = endSeg - startSeg + 1
local isSheetRegion = structure.GetSecondaryStructure( idx ) == 'E'
local isHelixRegion = structure.GetSecondaryStructure( idx ) == 'H'
if not isSheetRegion and not isHelixRegion then
-- actions that only make sense for loops
allowedActions[ #allowedActions + 1 ] = "GeneralHinge"
end
--if not isHelixRegion then
-- actions that don't make sense for helices (only sheets and loops)
-- allowedActions[ #allowedActions + 1 ] = "SheetStraightener"
-- end
if regionLength > 6 and isHelixRegion then
-- actions that only make sense for helices (and not just "curls" of helix-ness)
allowedActions[ #allowedActions + 1] = "HelixUniform"
end
if regionLength > 5 and isHelixRegion then
-- actions that only make sense for helices (and not just "curls" of helix-ness)
allowedActions[ #allowedActions + 1] = "HelixCompressor"
allowedActions[ #allowedActions + 1] = "HelixFixer"
end
if regionLength >= 5 then
allowedActions[ #allowedActions + 1] = "FlexerBands"
end
return allowedActions
end
function PickLocalOperation( idx )
if idx == nil then idx = randomUnlockedSeg( ) end
local allowedActions = {
"BIS",
"BandBetween",
"IdealizePoint",
"RebuildSimple",
"RebuildSimple2",
}
if (idx > 1) and (idx < segCt) and (structure.GetAminoAcid( idx ) == 'g') then
allowedActions[ #allowedActions + 1 ] = "GlycineHinge"
end
if not IsNoMutate( idx ) then
allowedActions[ #allowedActions + 1 ] = "SpotMutator"
end
if PuzzleHasContactMap and SegHasContactData( idx, CONTACTMAP_THRESHOLD ) then
allowedActions[ #allowedActions + 1] = "ContactTwoSegs"
end
return PickActionFromList( allowedActions, idx )
end
function PickMidsizeOperation( idx )
if idx == nil then idx = randomUnlockedSeg( ) end
local allowedActions = {
"PushPull",
"VoidCrusher",
"RebuildInRange",
"IdealizeInRange",
}
if PuzzleHasContactMap then
allowedActions[ #allowedActions + 1] = "ContactMapTwoRegions"
allowedActions[ #allowedActions + 1] = "ContactMapRebuildRange"
allowedActions[ #allowedActions + 1] = "ContactMapRebuildSS"
if SegHasContactData( idx, CONTACTMAP_THRESHOLD ) then
allowedActions[ #allowedActions + 1] = "ContactMapForSeg"
end
end
allowedActions = AddRegionBanderActions( allowedActions, idx )
return PickActionFromList( allowedActions, idx )
end
function PickLargeScaleOperation( )
local allowedActions = {
"MultiBander",
"CentralBander",
"EveryXToY",
"GentleComp",
"CoreStirrer",
"LSQuaker",
-- "LocalQuake" -- too slow for standard use.
}
if #sheetStarts > 1 then allowedActions[#allowedActions + 1] = "SheetSewer" end
if PuzzleHasContactMap then
allowedActions[ #allowedActions + 1] = "ContactMapper"
allowedActions[ #allowedActions + 1] = "SomeContactMapBands"
end
return PickActionFromList( allowedActions, nil )
end
function PickTargetedOperation( idx )
if idx == nil or idx == 0 then idx = randomLowScoringSeg( random(10) ) end
if random( ) < PROB_LOCAL / ( PROB_MIDSIZE + PROB_LOCAL ) then
return PickLocalOperation( idx )
else
return PickMidsizeOperation( idx )
end
end
function PickNormalOperation( idx )
if idx == nil or idx == 0 then idx = randomUnlockedSeg( ) end
local which = random( )
if which < PROB_TARGETED then
return PickTargetedOperation( idx )
elseif which < PROB_TARGETED + PROB_LOCAL then
return PickLocalOperation()
elseif which < PROB_TARGETED + PROB_LOCAL + PROB_MIDSIZE then
return PickMidsizeOperation()
else
return PickLargeScaleOperation()
end
end
function PickContactMapOperation( idx )
if idx == nil or idx == 0 then idx = randomContactableSeg( ) end
local allowedActions = {
"ContactMapper",
"SomeContactMapBands",
"ContactMapForSeg",
"ContactTwoSegs",
"ContactMapTwoRegions",
"ContactMapRebuildRange",
"ContactMapRebuildSS",
"ContactMapRebuildAll",
}
return PickActionFromList( allowedActions, idx )
end
function PickEarlyGameOperation( idx )
if idx == nil or idx == 0 then idx = randomUnlockedSeg( ) end
local allowedActions = {
"CentralBander",
"EveryXToY",
"GentleComp",
"PushPull",
"VoidCrusher",
"RebuildInRange",
"IdealizeInRange",
}
if PuzzleHasContactMap then
allowedActions[ #allowedActions + 1] = "ContactMapper"
allowedActions[ #allowedActions + 1] = "SomeContactMapBands"
allowedActions[ #allowedActions + 1] = "ContactMapTwoRegions"
allowedActions[ #allowedActions + 1] = "ContactMapRebuildRange"
allowedActions[ #allowedActions + 1] = "ContactMapRebuildSS"
allowedActions[ #allowedActions + 1] = "ContactMapRebuildAll"
end
return PickActionFromList( allowedActions, idx )
end
function PickLowCIOperation( idx )
if idx == nil or idx == 0 then idx = randomUnlockedSeg( ) end
local allowedActions = { }
if PuzzleHasContactMap then
allowedActions[ #allowedActions + 1] = "ContactMapper"
allowedActions[ #allowedActions + 1] = "SomeContactMapBands"
allowedActions[ #allowedActions + 1] = "ContactMapTwoRegions"
allowedActions[ #allowedActions + 1] = "ContactMapRebuildRange"
if SegHasContactData( idx, CONTACTMAP_THRESHOLD ) then
allowedActions[ #allowedActions + 1] = "PushPullContactMap"
allowedActions[ #allowedActions + 1] = "VoidCrusherContactMap"
allowedActions[ #allowedActions + 1] = "ContactMapForSeg"
else
allowedActions[ #allowedActions + 1] = "PushPull"
allowedActions[ #allowedActions + 1] = "VoidCrusher"
end
else
allowedActions[ #allowedActions + 1] = "CentralBander"
allowedActions[ #allowedActions + 1] = "EveryXToY"
allowedActions[ #allowedActions + 1] = "GentleComp"
allowedActions[ #allowedActions + 1] = "PushPull"
allowedActions[ #allowedActions + 1] = "VoidCrusher"
end
allowedActions[ #allowedActions + 1] = "RebuildSimpleRange"
allowedActions[ #allowedActions + 1] = "IdealizeSimpleRange"
return PickActionFromList( allowedActions, idx )
end
function PickCompressOperation( idx )
if idx == nil or idx == 0 then idx = randomUnlockedSeg( ) end
local allowedActions = {
"CentralBander",
"EveryXToY",
"GentleComp",
"PushPull",
"VoidCrusher",
"LocalQuake",
}
if PuzzleHasContactMap then
allowedActions[ #allowedActions + 1] = "ContactMapper"
allowedActions[ #allowedActions + 1] = "SomeContactMapBands"
allowedActions[ #allowedActions + 1] = "ContactMapTwoRegions"
end
return PickActionFromList( allowedActions, idx )
end
function PickLateGameOperation( idx )
if idx == nil or idx == 0 then idx = randomUnlockedSeg( ) end
local allowedActions = {
"CentralBander",
"EveryXToY",
"PushPull",
"VoidCrusher",
"BIS",
"BandBetween",
"RebuildSimple",
"RebuildSimple2",
}
if PuzzleHasContactMap and SegHasContactData( idx, CONTACTMAP_THRESHOLD ) then
allowedActions[ #allowedActions + 1] = "ContactTwoSegs"
end
allowedActions = AddRegionBanderActions( allowedActions, idx )
return PickActionFromList( allowedActions, idx )
end
function PickRandomRebuildOperation( idx )
changeSucceeded = false
if idx == nil or idx == 0 then idx = randomUnlockedSeg( ) end
local rebuildLength = random( math.floor( REBUILD_MAX_SEGLEN / 2 ) )
changeSucceeded = PickRebuildPointAction( idx, rebuildLength )
ResetCuts( )
if not changeSucceeded then print( " CHANGE FAILED" ) end
return changeSucceeded
end
function PickRandomLoopRebuildOperation( idx )
maxFall = REBUILD_MAX_EARLY_SCOREDROP
changeSucceeded = false
if idx == nil or idx == 0 then
local segList = {}
for i=1, segCt do
if IsMovableSeg( i ) and structure.GetSecondaryStructure( i ) == 'L' then
segList[ #segList + 1 ] = i
end
end
idx = segList[ random( #segList ) ]
end
startIdx, endIdx = GetRegionForOperation( idx )
startIdx = math.max( idx - math.floor( REBUILD_MAX_SEGLEN / 2 ), startIdx )
endIdx = math.min( idx + math.floor( REBUILD_MAX_SEGLEN / 2 ), endIdx )
changeSucceeded = PickRebuildRangeAction( startIdx, endIdx, maxFall )
-- clean up any mess we may have left behind...
ResetCuts( )
if not changeSucceeded then print( " CHANGE FAILED" ) end
return changeSucceeded
end
------------------------------------------------------------------------------
-- SLOW ENDGAME WIGGLERS
------------------------------------------------------------------------------
--------------------------------- FUSES ------------------------------------
function PinkFuse( iters )
recentbest.Save()
ShakeWiggleFuse( 0.10, 0.70, iters )
WiggleShakeWiggleFuse( iters )
recentbest.Restore()
ShakeWiggleFuse( 0.30, 0.60, iters )
WiggleShakeWiggleFuse( iters )
recentbest.Restore()
WiggleWiggleWiggleFuse( 0.50, 0.70, iters )
WiggleShakeWiggleFuse( iters )
recentbest.Restore()
WiggleWiggleWiggleFuse( 0.70, 0.50, iters )
WiggleShakeWiggleFuse( iters )
recentbest.Restore()
SetCI( 1.0 )
end
function CementCrusherFuse( iters )
CIlow = {0.98, 0.89, 0.55, 0.34, 0.21, 0.13, 0.08, 0.05, 0.03, 0.02, 0.01}
for i=1, #CIlow do
WiggleWiggleSimpleFuse( CIlow[i], 1.00, 1, iters )
end
end
function EvoSqueezeHwpFuse( iters )
WiggleWiggleSimpleFuse( 0.03, 1.00, 4, iters ) -- 10 for LWP, 14 for HWP
end
function MonsterBagOfFuses( iters )
save.Quicksave( QS_ShortUseTemp1 )
local startScore = getScore()
ShakeWiggleFuse( 0.3, 0.6, iters )
WiggleShakeWiggleFuse( iters )
local newScore = getScore( )
if newScore > startScore then save.Quicksave( QS_ShortUseTemp1 ) end
save.Quickload( QS_ShortUseTemp1 )
print("First fuse got gain "..getScore() - startScore )
startScore = getScore()
WiggleWiggleWiggleFuse( 0.5, 0.7, iters )
local newScore = getScore( )
if newScore > startScore then save.Quicksave( QS_ShortUseTemp1 ) end
save.Quickload( QS_ShortUseTemp1 )
print("Second fuse got gain "..getScore() - startScore )
startScore = getScore()
ShakeWiggleFuse( 0.05, 1, iters )
newScore = getScore( )
if newScore > startScore then save.Quicksave( QS_ShortUseTemp1 ) end
save.Quickload( QS_ShortUseTemp1 )
print("Third fuse got gain "..getScore() - startScore )
startScore = getScore()
WiggleWiggleWiggleFuse( 0.7, 0.5, iters )
WiggleShakeWiggleFuse( iters )
newScore = getScore( )
if newScore > startScore then save.Quicksave( QS_ShortUseTemp1 ) end
save.Quickload( QS_ShortUseTemp1 )
print("4th fuse got gain "..getScore() - startScore )
startScore = getScore()
ShakeWiggleFuse( 0.07, 1, iters )
newScore = getScore( )
if newScore > startScore then save.Quicksave( QS_ShortUseTemp1 ) end
save.Quickload( QS_ShortUseTemp1 )
print("5th fuse got gain "..getScore() - startScore )
startScore = getScore()
WiggleWiggleWiggleFuse( 0.3, 1, iters )
newScore = getScore( )
if newScore > startScore then save.Quicksave( QS_ShortUseTemp1 ) end
save.Quickload( QS_ShortUseTemp1 )
print("6th fuse got gain "..getScore() - startScore )
startScore = getScore()
BlueFuse( )
newScore = getScore( )
if newScore > startScore then save.Quicksave( QS_ShortUseTemp1 ) end
save.Quickload( QS_ShortUseTemp1 )
print("Blue fuse got gain "..getScore() - startScore )
startScore = getScore()
PinkFuse( iters )
newScore = getScore( )
if newScore > startScore then save.Quicksave( QS_ShortUseTemp1 ) end
save.Quickload( QS_ShortUseTemp1 )
print("Pink fuse got gain "..getScore() - startScore )
startScore = getScore()
CementCrusherFuse( iters )
newScore = getScore( )
if newScore > startScore then save.Quicksave( QS_ShortUseTemp1 ) end
save.Quickload( QS_ShortUseTemp1 )
print("CementCrush fuse got gain "..getScore() - startScore )
startScore = getScore()
SetCI( 0.2 )
ShakeOrMutate( 2 )
SetCI( 1.0 )
structure.WiggleAll( 25 )
newScore = getScore( )
if newScore > startScore then save.Quicksave( QS_ShortUseTemp1 ) end
save.Quickload( QS_ShortUseTemp1 )
print("Closing shake/wiggle got gain "..getScore() - startScore )
end
function FuseBurstIterateCi ( baseIters )
recentbest.Save()
ci = 0.01
while ( ci < 1 ) do
SetCI( ci )
structure.WiggleAll ( baseIters )
ShakeOrMutate( baseIters )
ci = ci * 2
end
SetCI( 1.0 )
structure.WiggleAll ( 2*baseIters )
ShakeOrMutate( 1 )
recentbest.Restore()
end
function QuickFuseBurst( baseIters )
WiggleShakeSimpleFuse( 0.01, baseIters )
WiggleShakeSimpleFuse( 0.50, baseIters )
WiggleShakeSimpleFuse( 1.00, baseIters*2 )
end
function FreezeBurstFuse( baseIters )
local frozenCt = math.floor( segCt/ 2 ) - 1
while ( frozenCt > 1 ) do
print( "Freezing "..frozenCt.." segments")
local currScore = getScore( )
recentbest.Save()
local idxList = {}
getBestScoringAAs( idxList, frozenCt ) -- should be able to ask for "movable" aas!
for i = 1, frozenCt do
freeze.Freeze( idxList[i] , true , true )
end
QuickFuseBurst( baseIters )
recentbest.Restore()
freeze.UnfreezeAll ()
local endScore = getScore( )
if endScore > currScore then
print( ">>> Gained "..TrimNum( endScore - currScore ).. " points (now "..TrimNum( endScore )..")" )
end
frozenCt = math.floor( frozenCt / 2 )
end
freeze.UnfreezeAll ()
end
function FullIterativeCutAndWiggle( baseIters, minGain, maxShift, randomizeShifts )
save.Quicksave( QS_ShortUseTemp1 )
local fakeprimes = {1,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101}
local shiftsLt = {}
for i=1, #fakeprimes do -- called fake because 1 is among them
if fakeprimes[ i ] > maxShift then break end
shiftsLt[ #shiftsLt + 1 ] = fakeprimes[ i ]
end
if randomizeShifts then randomizeIndexList( shiftsLt ) end
local startScore = getScore()
local bestscore = startScore
for i=1, #shiftsLt do
save.Quickload( QS_ShortUseTemp1 )
local gain = 0.0
local currscore = getScore()
local newscore = currscore
local shift = shiftsLt[ i ]
for offset = 1, shift do
save.Quickload( QS_ShortUseTemp1 )
print("C&W: shift="..shift.." offset="..offset.." score="..bestscore)
BasicCutAndWiggleFuse( baseIters, shift, offset )
newscore = getScore()
if newscore > bestscore then
save.Quicksave( QS_ShortUseTemp1 )
bestscore = newscore
end
end -- FOR offset
gain = bestscore - currscore
if TrimNum( gain ) > 0 then
print( "C&W (shift "..shift..") had gain="..TrimNum( gain ) )
end
end -- FOR i < #shiftsLt
save.Quickload( QS_ShortUseTemp1 )
local endScore = getScore( )
if endScore > startScore then
print( "C&W End Score="..TrimNum( endScore ).." gain="..TrimNum( endScore - startScore ) )
end
end
--------------------------------- WALKERS ------------------------------------
function WiggleLocalByChunk( iters, startSeg, endSeg, chunkSize, passCount, addFreeze )
currOffset = 0
for i=1, passCount do
if (passCount == 1 or chunkSize == 1) then currOffset = 0
else currOffset = random(chunkSize - 1)
end
ctBlocks = (endSeg - startSeg) / chunkSize
for idx = 0,ctBlocks do
startIdx = startSeg + currOffset + chunkSize*idx
WiggleRange(iters, startIdx, startIdx + chunkSize, addFreeze, false )
end
end
end
function WormWiggle( )
local iters = 20
local lens = {2,5,11,3,13,4,7,1,6}
save.Quicksave( QS_ShortUseTemp1 )
for i=1, #lens do
local currScore = getScore( )
WiggleLocalByChunk( iters, 1, segCt, lens[ i ], 1, false )
local newScore = getScore( )
local gain = newScore - currScore
if gain > 0 then
save.Quicksave( QS_ShortUseTemp1 )
if TrimNum( gain ) > 0 then print( "Worm " .. i .. ": len=" .. lens[ i ] .. " gain=" .. TrimNum( gain ) ) end
else
save.Quickload( QS_ShortUseTemp1 )
end
end
save.Quickload( QS_ShortUseTemp1 )
end
function PutSingleRandomBandToSeg( idx )
changeSucceeded = false
local strength = random( 0.5 * BAND_STRENGTH_DEFAULT,
1.5 * BAND_STRENGTH_DEFAULT,
true )
local doBIS = random( ) < PROB_BIS_NOT_BETWEEN
if doBIS then
changeSucceeded = PutBandInSpace( idx, BIS_LENGTH, strength )
else
if SegHasContactData( idx, CONTACTMAP_THRESHOLD ) and
random( ) > PROB_CHOOSE_CONTACTMAP
then
local doSidechain = random( ) < PROB_BETWEEN_USES_TIP
changeSucceeded = PutSomeContactMapBands( CONTACTMAP_THRESHOLD, strength, 1, doSidechain )
else
local atom = PickAtomNumberForBand( idx )
changeSucceeded = PutBandToRandomSeg( idx, strength, atom )
end
end
return changeSucceeded
end
function BandedWorm( )
pattern={ 2,5,11,3,13,4,7,1,6 }
recentbest.Save( )
SetCI( 1.0 )
SaveBest( )
local ss = getScore()
for w = 1, #pattern do
len = pattern[ w ]
local sw = getScore()
local swCurr = sw
print( "Starting BandedWorm of len " .. len .. ", score: ".. TrimNum( getScore() ) )
for s=1, segCt - len + 1 do
selection.DeselectAll()
selection.SelectRange( s, s + len - 1 )
local idx = random( s, s + len - 1 )
if random( ) < PROB_PUTBAND then
PutSingleRandomBandToSeg( idx )
structure.LocalWiggleSelected( 2 )
DelBands( )
structure.WiggleAll( 2 )
end
structure.LocalWiggleSelected( 2 )
local swNew = getScore( )
local gain = swNew - swCurr
if gain > 0 then
structure.LocalWiggleSelected( 20 )
recentbest.Restore( )
DelBands( )
swNew = getScore( )
gain = swNew - swCurr
if TrimNum( gain ) > 0 then print( ">>>> At " .. s .. ": Gained ".. TrimNum( gain ) ) end
SaveBest( )
swCurr = swNew
end
recentbest.Restore( )
DelBands( )
end
print( "Pattern gain: ".. getScore( ) - sw )
SaveBest( )
end
selection.DeselectAll( )
local endScore = getScore( )
print( "Total BandedWorm gain: " .. TrimNum( endScore - ss ) )
if endScore - ss > 5.0 then BandedWorm( ) end -- my very own tail-recursion
end
----------------- Other March-Wiggle Actions -----------------------
function PointRebuilder( )
local movableSegs = { }
save.Quicksave( QS_NeighborTemp1 )
for i=1, segCt do
if IsMovableSeg( i ) then movableSegs[ #movableSegs + 1 ] = i end
end
randomizeIndexList( movableSegs )
print( "PR: ".. #movableSegs )
for i=1, #movableSegs do
print( "Rebuild "..i.." at "..movableSegs[ i ] )
save.Quickload( QS_NeighborTemp1 )
local currScore = getScore( )
if random( ) > 0.50 then
RebuildSimple( movableSegs[ i ], 1 )
else
RebuildSimple2( movableSegs[ i ], 1 )
end
BlueFuse( )
local endScore = getScore( )
local gain = endScore - currScore
if gain > 0 then
save.Quicksave( QS_NeighborTemp1 )
if TrimNum( gain ) > 0 then
print( ">>>> RebuildSimple gain "..TrimNum( gain ).." score="..endScore )
end
end
end
save.Quickload( QS_NeighborTemp1 )
end
--------------------------------------------------------------------------------------------------------
-- NEIGHBOR-GENERATION
--------------------------------------------------------------------------------------------------------
function GetNeighborState( threshold, NeighborGenerationFunction )
local oldCreditBest = creditbest.GetScore( ) -- to find out if we've missed transients
local oldScoreMat = {}
local oldQuickScore = getQuickScore( )
local oldScore = getScore( )
ConstructScoreMatrix( oldScoreMat )
SetCI( 1.0 )
DelBands( )
selection.DeselectAll( )
ResetFrozenness( )
recentbest.Save( )
local rbscore = getRBScore( )
save.Quicksave( QS_NeighborTemp1 )
local goodNeighbor = false
while not goodNeighbor do
local oldTime = os.time( )
local changeSucceeded = false
goodNeighbor = false
save.Quickload( QS_NeighborTemp1 )
changeSucceeded = NeighborGenerationFunction( 0 )
if changeSucceeded then
local newScore = getScore( )
goodNeighbor = IsProteinChanged( oldQuickScore, oldScoreMat, threshold )
if not goodNeighbor then
AddFailToStats( )
print( "NEIGHBOR REJECTED (score:"..newScore..")")
else
local deltaScore = newScore - oldScore
local deltaTime = os.time() - oldTime
AddActionToStats( deltaScore, deltaTime )
end
else
AddFailToStats( )
end
end -- END while not goodNeighbor
local newScore = getScore( )
local newCreditBest = creditbest.GetScore( )
if newCreditBest ~= oldCreditBest and newCreditBest ~= newScore then
-- we missed a transient
print( " !!!(1) CreditBest changed: old="..oldCreditBest.." new="..newCreditBest )
end
DoCleanupWiggle( USE_SLOWWIGGLE, SLOWWIGGLE_ITERS, SLOWWIGGLE_GAIN )
newScore = getScore( )
newCreditBest = creditbest.GetScore( )
if newCreditBest ~= oldCreditBest and newCreditBest ~= newScore then
-- we missed a transient
print( " !!!(2) CreditBest changed: old="..oldCreditBest.." new="..newCreditBest )
end
DebugPrint( "Neighbor Score: " .. newScore)
end
----------------------------------------------------------------------
--
---- SIMULATED ANNEALING ALGORITHM FUNCTIONS
--
----------------------------------------------------------------------
-- Real research could be done (rosetta folks have done) on what a "good"
-- Temperature function is for these purposes. 1/x does well enough for me.
-- Note that "num" goes from 0 to 1 over the life of the SA function.
function T_OneOverX_Old( num )
-- This goes +INF -> 0 as 1/x (for +INF ~= 1000000)
return ( 1.0 / (num + 0.000001) ) - 1.0
end
function T_OneOverX( num ) -- trying to make start a bit less "wild". max=100
return ( 1.0 / (num + 0.01) ) - ( 1.0 / 1.01 )
end
function T_OneOverX2( num )
x = ( 1.0 / (num + 0.001) ) - 1.0
return x * x
end
function T_MiddlingX( num )
return 10.0 * (1.0 - num)
end
function T_LowX( num )
return 1.0 - num
end
function T_ColdX( num )
return 0.1 * (1.0 - num)
end
function T_Zero( num )
return 0.0 -- causes SA to drop to normal march-forward behavior.
end
-- this is the standard probability function in Simulated Annealing algorithms
function P( ecurr, enew, T )
if enew < ecurr then return 1.0 end
return math.exp( (ecurr - enew)/T )
end
-- must negate score to function as a Simulated Annealing energy function
function E( )
return 8000.0 - getScore( ) -- 8000 is irrelevant, just for scaling
end
function SA( kmax, emax, qs_best, failNoGainPt, NeighborGenerationFunction, TempFunc )
-- emax is expected to "never happen"
if TempFunc == nil then TempFunc = T_OneOverX end
local estart = E( )
local ecurr = estart
save.Quicksave( QS_TopLevelTemp ) -- a private slot
local ebest = ecurr
local startTime = os.time( )
local k = 0.0
while k < kmax and ecurr > emax do
save.Quickload( QS_TopLevelTemp )
local T = TempFunc( k / kmax )
print("##"..(k+1)..": temp="..TrimNum( T ).." score="..TrimNum( getScore( ) ).." time: "..os.time( )-startTime )
GetNeighborState( 0.1, NeighborGenerationFunction )
local enew = E( )
local p = P(ecurr, enew, T)
local r = random( )
if p > r then
print( ">>>> accepting state with score " .. TrimNum( getScore( )) )
save.Quicksave( QS_TopLevelTemp )
ecurr = enew
end
if k +1 >= failNoGainPt and ebest >= estart then
print( "<< Reached step "..(k+1).." with no improvement - bailing" )
return k+1 -- give up: we've not achieved any improvement and we think we never will
end
if enew < ebest then
save.Quicksave( qs_best )
ebest = enew
end
k = k + 1.0
end
return k
end
function PerformSA( failNoGainPt, ctRestarts,
stepsPerRun, minGainPerRun,
NeighborGenerationFunction, TempFunc )
if minGainPerRun == nil then minGainPerRun = 0 end
local saInitialTime = os.time( )
local saInitialScore = getScore( )
local bestScore = saInitialScore
local newscore = 0.0
local stepCount = 0
SaveBest( )
for i=1, ctRestarts do
LoadBest( )
local saRunStartScore = getScore( )
local saRunStartTime = os.time( )
print( "START: SA#"..i.." score="..TrimNum( saRunStartScore ).." time="..saRunStartTime - saInitialTime )
stepCount = stepCount + SA( stepsPerRun, -999999.0, QS_Best, failNoGainPt,
NeighborGenerationFunction, TempFunc )
LoadBest( )
newscore = getScore( )
gain = newscore - bestScore
if newscore > bestScore then
bestScore = newscore
end
print( "FINISH: SA#"..i.." score="..TrimNum( newscore )..
" gain=".. newscore - saRunStartScore .." time="..os.time( ) - saRunStartTime )
if gain < minGainPerRun then
print( " Insufficient Gain "..gain.." - halting SA" )
break
end
end
print( "SA: steps=".. stepCount ..
" gain="..getScore( ) - saInitialScore .." time="..os.time( ) - saInitialTime )
end
function PerformMarchingRebuild( maxRunsNoGain )
if neighborThreshold == nil then neighborThreshold = 0.01 end
local ctRunsWithoutGain = 0
local mrInitialTime = os.time( )
local mrInitialScore = getScore( )
local bestScore = mrInitialScore
local len = REBUILD_MAX_SEGLEN
local segStartList = {}
for i=1, segCt-len+1, math.floor( (len+1) / 2 ) do
if IsMovableSeg( i ) then
segStartList[ #segStartList+1] = i
end
end
randomizeIndexList( segStartList )
print( "MR START " .. " score=".. TrimNum( mrInitialScore ).." time=".. os.time( ) - mrInitialTime )
local ct = 0
repeat
ct = ct + 1
if ctRunsWithoutGain >= maxRunsNoGain then return end
LoadBest( )
local prevscore = getScore()
print( "MR# ".. ct .. ": score=".. TrimNum( prevscore ).." failcount="..
ctRunsWithoutGain.." time="..os.time( ) - mrInitialTime )
changeSucceeded = PickRebuildRangeAction( segStartList[ ct ], segStartList[ ct ] + len - 1 )
if changeSucceeded then -- we don't want to preserve results that left cuts behind!
local currscore = getScore()
if currscore > bestScore then
ctRunsWithoutGain = 0
gain = currscore - bestScore
bestScore = currscore
SaveBest( )
print( ">>>> Gain of ".. gain .. " points" )
else
gain = 0
ctRunsWithoutGain = ctRunsWithoutGain + 1
LoadBest( )
end
if ct == #segStartList then
-- re-randomize the seglist, pick a different len, and start over
segStartList = {}
if len > 3 then len = len - 2 else len = REBUILD_MAX_SEGLEN - 1 end
for i=1, segCt-len+1 do segStartList[ i ] = i end
randomizeIndexList( segStartList )
ct = 0
end
end
if ct >= #segStartList then break end
until ctRunsWithoutGain > maxRunsNoGain
print( "MR FINISH: gain ="..bestScore-mrInitialScore.." time="..os.time( ) - mrInitialTime )
end
function PerformMarchForward( maxRunsNoGain, stepsWanted, neighborThreshold )
if neighborThreshold == nil then neighborThreshold = 0.01 end
local ctRunsWithoutGain = 0
local mfInitialTime = os.time( )
local mfInitialScore = getScore( )
local bestScore = mfInitialScore
SaveBest( )
print( "MF START " .. " score=".. TrimNum( mfInitialScore ).." time="..os.time( ) - mfInitialTime )
for i=1, stepsWanted do
if ctRunsWithoutGain >= maxRunsNoGain then break end
LoadBest( )
local prevscore = getScore()
print( "MF STEP ".. i.. " score=".. TrimNum( prevscore ).." time="..os.time( ) - mfInitialTime )
GetNeighborState( neighborThreshold, PickLateGameOperation )
local currscore = getScore()
if currscore > bestScore then
ctRunsWithoutGain = 0
gain = currscore - bestScore
bestScore = currscore
SaveBest( )
print( ">>>> Gain of ".. gain .. " points" )
else
gain = 0
ctRunsWithoutGain = ctRunsWithoutGain + 1
LoadBest( )
end
end
print( "MF FINISH: gain="..bestScore-mfInitialScore.." time="..os.time( ) - mfInitialTime )
end
function PerformMarchWiggle( lastScore, marchThreshold, whichWiggle )
SetCI( 1.0 )
algName = whichWiggle
LoadBest( )
local mwInitialScore = getScore()
local startScore = mwInitialScore
local mwInitialTime = os.time( )
local wiggleStartTime = mwInitialTime
local iters = FINALWIGGLE_ITERS
local rewiggleGain = FINALRUN_WIGGLE_GAIN
print( "MW START " .. " score=".. TrimNum( mwInitialScore ).." time="..os.time( ) - mwInitialTime )
-- see if we should attempt some PerformMarchForward
if mwInitialScore - lastScore > marchThreshold then
PerformOperation( "PerformMarchForward" )
lastScore = getScore()
wiggleStartTime = mwInitialTime
print("Wiggle beginning: score="..TrimNum( lastScore ).." time="..os.time( ) - wiggleStartTime )
startScore = lastScore
if getScore() > startScore then
DelBands( )
ResetFrozenness( )
selection.DeselectAll( )
SaveBest( )
else
LoadBest( )
end
end
-- perform a default version of whatever wiggle the user asked for
print( " "..whichWiggle )
if whichWiggle == "IterativeWiggleLocalByChunk-freeze" then
IterativeWiggleLocalByChunk( iters, 1, segCt, rewiggleGain, random(3,5), true )
elseif whichWiggle == "IterativeWiggleLocalByChunk" then
IterativeWiggleLocalByChunk( iters, 1, segCt, rewiggleGain, random(3,5), false )
elseif whichWiggle == "FreezeBurst" then
FreezeBurstFuse( iters )
elseif whichWiggle == "IterativeWiggleAll" then
IterativeWiggleAll( iters, rewiggleGain, true, true)
elseif whichWiggle == "MonsterBagOfFuses" then
MonsterBagOfFuses( iters )
elseif whichWiggle == "FuseBurstIterateCi" then
FuseBurstIterateCi ( iters )
elseif whichWiggle == "FullIterativeCutAndWiggle" then
FullIterativeCutAndWiggle( iters, rewiggleGain, 17, true)
ResetCuts( ) -- really shouldn't be any cuts left lying around...
elseif whichWiggle == "WormWiggle" then
WormWiggle( )
elseif whichWiggle == "BandedWorm" then
BandedWorm( )
elseif whichWiggle == "PointRebuilder" then
PointRebuilder( )
else
print("!! no such wiggle")
end
if getScore( ) > startScore then
DelBands( ) -- shouldn't be any, but just to be safe...
ResetFrozenness( )
selection.DeselectAll( )
SaveBest( )
else
SaveBest( )
end
local bestScore = getScore( )
local deltaScore = bestScore - startScore
local deltaTime = os.time() - mwInitialTime
AddActionToStats( deltaScore, deltaTime )
print( "MW FINISH: gain="..bestScore-mwInitialScore.." time="..os.time( ) - mwInitialTime )
return lastScore
end
function PerformMarchWiggleSequence( algSequence )
local marchInitialTime = os.time( )
local marchInitialScore = getScore( )
print( "MarchWiggle START " .. " score: ".. TrimNum( marchInitialScore ).." time: "..os.time( ) - marchInitialTime )
if algSequence == nil or #algSequence == 0 then
algSequence = {
"FullIterativeCutAndWiggle",
"BandedWorm",
"FreezeBurst",
"IterativeWiggleAll",
"WormWiggle",
"FuseBurstIterateCi",
"MonsterBagOfFuses",
"IterativeWiggleLocalByChunk-freeze",
"IterativeWiggleLocalByChunk",
"BandedWorm",
"PointRebuilder",
}
end
USE_SLOWWIGGLE = true
local lastScore = getScore() -- first call won't do march forward
local minGain = MARCHWIGGLE_MINGAIN
for i=1, #algSequence do
lastScore = PerformMarchWiggle( lastScore, minGain, algSequence[ i ] )
end
print( " MarchWiggle FINISH: gain="..lastScore-marchInitialScore.." time="..os.time( ) - marchInitialTime )
end
function PerformRebuildAndSA( )
SaveBest( )
local dsInitialTime = os.time( )
local dsInitialScore = getScore( )
print( " Begin Rebuild+SA sequence: time="..dsInitialTime )
USE_SLOWWIGGLE = false
PerformOperation( "SARebuild" )
PerformOperation( "SAEarly" )
PerformOperation( "SAMidgame" )
ReportStats( )
ResetStats( ) -- clear out old preconceptions of how well wigglers and such behave
-- set up for slower, more careful runs with fusing
USE_SLOWWIGGLE = true
if REBUILD_MAX_SEGLEN > 4 then
REBUILD_MAX_SEGLEN = math.floor ( REBUILD_MAX_SEGLEN / 2 )
end
PerformOperation( "SAMidgame" )
PerformOperation( "SAVeryLate" )
print( " End Rebuild+SA sequence: gain="..getScore( ) - dsInitialScore.." time="..os.time( ) - dsInitialTime )
end
function PerformOperation( OperationChosen )
local moInitialScore = getScore( )
local moInitialTime = os.time( )
local numSteps = 0
local failNoGain = 0
SaveBest( )
if OperationChosen == "SARebAndNormal" then
PerformRebuildAndSA( )
elseif OperationChosen == "SALoopRebuild" then
for i=1, #loopStarts do
if IsMovableSeg( loopStarts[i] ) or IsMovableSeg( loopEnds[i] ) then
numSteps = numSteps + 1 + math.floor( (loopEnds[i]-loopStarts[i])/2 )
end
end
numSteps = math.min( numSteps, SA_STEPSPERRUN )
failNoGain = math.floor( numSteps / 2 )
print( "LoopRebuilds for "..numSteps.." steps; will fail if no gain by "..failNoGain )
PerformSA( failNoGain,
SA_REBUILD_NUMSTARTS, numSteps, SA_MINGAIN,
PickRandomLoopRebuildOperation, T_OneOverX )
print( "LoopRebuild finished" )
elseif OperationChosen == "SARebuild" then
numSteps = math.min( math.floor( (#NonLockedSegList+2) / 2 ), SA_STEPSPERRUN )
failNoGain = math.floor( numSteps / 2 )
print( "SARebuild for "..numSteps.." steps; fail if no gain by "..failNoGain )
PerformSA( failNoGain,
SA_REBUILD_NUMSTARTS, numSteps, SA_MINGAIN,
PickRandomRebuildOperation, T_OneOverX )
print( "SARebuild finished" )
elseif OperationChosen == "SACompress" then
print( "SA-Compress for "..SA_STEPSPERRUN.." steps and "..SA_NUMSTARTS..
" runs; fail if no gain by "..SA_FAILIFNOGAIN )
local maxExpandOld = BAND_MAXEXPAND
BAND_MAXEXPAND = 1.0 + ( (BAND_MAXEXPAND - 1.0) / 2 )
PerformSA( SA_FAILIFNOGAIN,
SA_NUMSTARTS, SA_STEPSPERRUN, SA_MINGAIN,
PickCompressOperation, T_OneOverX )
BAND_MAXEXPAND = maxExpandOld
print( "SA-Compress finished" )
elseif OperationChosen == "SAEarly" then
print( "SA-Early for "..SA_STEPSPERRUN.." steps and "..SA_NUMSTARTS..
" runs; fail if no gain by "..SA_FAILIFNOGAIN )
PerformSA( SA_FAILIFNOGAIN,
SA_NUMSTARTS, SA_STEPSPERRUN, SA_MINGAIN,
PickEarlyGameOperation, T_OneOverX )
print( "SA-Early finished" )
elseif OperationChosen == "SALowCI" then
USE_LOWCI_WIGGLES = true
print( "SA-LowCI for "..SA_STEPSPERRUN.." steps and "..SA_NUMSTARTS..
" runs; fail if no gain by "..SA_FAILIFNOGAIN )
PerformSA( SA_FAILIFNOGAIN,
SA_NUMSTARTS, SA_STEPSPERRUN, SA_MINGAIN,
PickLowCIOperation, T_OneOverX ) --T_LowX )
USE_LOWCI_WIGGLES = false
print( "SA-LowCI finished" )
elseif OperationChosen == "SAMidgame" then
print( "SA-Midgame for "..SA_STEPSPERRUN.." steps and "..SA_NUMSTARTS..
" runs; fail if no gain by "..SA_FAILIFNOGAIN )
PerformSA( SA_FAILIFNOGAIN,
SA_NUMSTARTS, SA_STEPSPERRUN, SA_MINGAIN,
PickNormalOperation, T_OneOverX )
print( "SA-Midgame finished" )
elseif OperationChosen == "SALate" then
print( "SA-Late for "..SA_STEPSPERRUN.." steps and "..SA_NUMSTARTS..
" runs; fail if no gain by "..SA_FAILIFNOGAIN )
PerformSA( SA_FAILIFNOGAIN,
SA_NUMSTARTS, SA_STEPSPERRUN, SA_MINGAIN,
PickLateGameOperation, T_OneOverX )
print( "SA-Late finished" )
elseif OperationChosen == "SAVeryLate" then
print( "SA-VeryLate for "..SA_STEPSPERRUN.." steps and "..SA_NUMSTARTS..
" runs; fail if no gain by "..SA_FAILIFNOGAIN )
local qwg = QUICKWIGGLE_GAIN
local swg = SLOWWIGGLE_GAIN
QUICKWIGGLE_GAIN = 0.1
SLOWWIGGLE_GAIN = 0.01
USE_SLOWWIGGLE = true
PerformSA( SA_FAILIFNOGAIN,
SA_NUMSTARTS, SA_STEPSPERRUN, SA_MINGAIN,
PickNormalOperation, T_OneOverX )
PerformSA( SA_FAILIFNOGAIN,
SA_NUMSTARTS, SA_STEPSPERRUN, SA_MINGAIN,
PickLateGameOperation, T_OneOverX )
print( "SA-VeryLate finished" )
QUICKWIGGLE_GAIN = qwg
SLOWWIGGLE_GAIN = swg
elseif OperationChosen == "SAContact" then
print( "SA-Contact for "..SA_STEPSPERRUN.." steps and "..SA_NUMSTARTS..
" runs; fail if no gain by "..SA_FAILIFNOGAIN )
PerformSA( SA_FAILIFNOGAIN,
SA_NUMSTARTS, SA_STEPSPERRUN, SA_MINGAIN,
PickContactMapOperation, T_OneOverX )
print( "SA-Contact finished" )
elseif OperationChosen == "PerformMarchForward" then
USE_SLOWWIGGLE = true
PerformMarchForward( MF_FAILSBEFORERESTART, MF_STEPSPERRUN )
elseif OperationChosen == "MarchWiggle" then
PerformMarchWiggleSequence( ) -- support an algSequence here!
elseif OperationChosen == "PerformMarchingRebuild" then
PerformMarchingRebuild( REBUILD_FAILSBEFOREQUIT )
else
print( "!! Unknown operation" )
end
LoadBest( )
local endScore = getScore( )
end
-----------------------------------------------------------------------------
-- DIALOG BOX SEQUENCE FROM THE NETHERWORLD
-- Still missing:
-- Mutate-related options
-- Individual wiggles, banders, rebuilders, idealizers (especially the
-- slow wigglers in MarchWiggle)
-----------------------------------------------------------------------------
-- currently MarchOptions are unused
function PutMarchOptionsInBox( dlg )
dlg.sep = dialog.AddLabel( "-----------------March Options-----------------------" )
dlg.mfstepsper = dialog.AddSlider( "MarchSteps", MF_STEPSPERRUN, 5, 100, 0 )
dlg.mffails = dialog.AddSlider( "FailsBeforeQuit", MF_FAILSBEFORERESTART, 5, 100, 0 )
end
function SetMarchOptions( dlg )
MF_STEPSPERRUN = dlg.mfstepsper.value
MF_FAILSBEFORERESTART = dlg.mffails.value
DebugPrint("PerformMarchForward steps="..MF_STEPSPERRUN)
DebugPrint("PerformMarchForward failsallowed="..MF_FAILSBEFORERESTART)
end
function PutQuickWiggleOptionsInBox( dlg )
dlg.sepQWSA = dialog.AddLabel( "----------------Quick Wiggle During Step---------------------" )
--dlg.qwiter = dialog.AddSlider( "QuickWiggleIters", QUICKWIGGLE_ITERS, 1, 10, 0)
dlg.qwgain = dialog.AddSlider( "QuickWiggleGain", QUICKWIGGLE_GAIN, 0, 1, 2)
end
function SetQuickWiggleOptions( dlg )
--QUICKWIGGLE_ITERS = dlg.qwiter.value
QUICKWIGGLE_GAIN = dlg.qwgain.value
-- DebugPrint("QuickIters="..QUICKWIGGLE_ITERS)
DebugPrint("QuickGain="..QUICKWIGGLE_GAIN)
end
function PutSlowWiggleOptionsInBox( dlg )
dlg.sepSWSA = dialog.AddLabel( "----------------Slow Wiggle After Step------------------------" )
--dlg.switer = dialog.AddSlider( "SlowWiggleIters", SLOWWIGGLE_ITERS, 2, 14, 0 )
dlg.swgain = dialog.AddSlider( "SlowWiggleGain", SLOWWIGGLE_GAIN, 0, 0.50, 2 )
dlg.comment = dialog.AddLabel( "Fuse-wiggle values are 'probabilities' - scaled to sum to 1" )
dlg.wloc = dialog.AddSlider( "LocalWiggler", PROB_SLOWWIGGLE_LOCAL, 0, 1, 2 ) -- rather slow but very good
dlg.wbfuse= dialog.AddSlider( "BlueFuse", PROB_SLOWWIGGLE_BLUEFUSE, 0, 1, 2 ) -- untested
dlg.wcwfuse= dialog.AddSlider( "C&WFuse", PROB_SLOWWIGGLE_CUTFUSE, 0, 1, 2 ) -- slow, moderately good
end
function SetSlowWiggleOptions( dlg )
--SLOWWIGGLE_ITERS = dlg.switer.value
SLOWWIGGLE_GAIN = dlg.swgain.value
local t1 = dlg.wloc.value
local t2 = dlg.wbfuse.value
local t3 = dlg.wcwfuse.value
if t1 + t2 + t3 == 0 then
t1 = .25 -- not a problem that don't sum to 1: they'll be scaled
t2 = .25
t3 = .25
end
PROB_SLOWWIGGLE_LOCAL = t1 / ( t1 + t2 + t3 )
PROB_SLOWWIGGLE_BLUEFUSE = t2 / ( t1 + t2 + t3 )
PROB_SLOWWIGGLE_CUTFUSE = t3 / ( t1 + t2 + t3 )
--DebugPrint( "SlowIters="..SLOWWIGGLE_ITERS )
DebugPrint( "SlowGain="..SLOWWIGGLE_GAIN )
DebugPrint( "Prob_slowwiggle-local="..PROB_SLOWWIGGLE_LOCAL )
DebugPrint( "Prob_slowwiggle-bluefulse="..PROB_SLOWWIGGLE_BLUEFUSE )
DebugPrint( "Prob_slowwiggle-cutfulse="..PROB_SLOWWIGGLE_CUTFUSE )
end
function PutBanderOptionsInBox( dlg )
dlg.sepBander = dialog.AddLabel( "----------------Bander Options---------------------" )
dlg.bdcpci = dialog.AddSlider( "CorePulling CI", COREPULLING_CI, 0.01, 1.0, 2 )
dlg.bdregstren = dialog.AddSlider( "RegionBandStrength", BAND_STRENGTH_REGION, 0.50, 2.0, 1 )
dlg.bdwigtryct = dialog.AddSlider( "BWig TryCount", BANDEDWIGGLE_TRYCT, 1, 10, 0 )
dlg.bdwigtargdelta = dialog.AddSlider( "BWig ScoreDelta", BANDEDWIGGLE_TARGET, 50, 1000, 0 )
dlg.bdwigmincompress = dialog.AddSlider( "MinCompress", BAND_MINCOMPRESS, 0.20, 1.00, 2 )
dlg.bdwigmaxexpand = dialog.AddSlider( "MaxExpand", BAND_MAXEXPAND, 1.00, 5.00, 2 )
end
function SetBanderOptions( dlg )
COREPULLING_CI = dlg.bdcpci.value
BAND_STRENGTH_REGION = dlg.bdregstren.value
BANDEDWIGGLE_TRYCT = dlg.bdwigtryct.value
BANDEDWIGGLE_TARGET = dlg.bdwigtargdelta.value
BAND_MINCOMPRESS = dlg.bdwigmincompress.value
BAND_MAXEXPAND = dlg.bdwigmaxexpand.value
DebugPrint("Core-pullling CI="..COREPULLING_CI )
DebugPrint("Region-bander strength="..BAND_STRENGTH_REGION )
DebugPrint("Banded Wiggler TryCount="..BANDEDWIGGLE_TRYCT )
DebugPrint("Banded Wiggler Target Delta="..BANDEDWIGGLE_TARGET )
DebugPrint("Min Compress="..BAND_MINCOMPRESS)
DebugPrint("Max Expand="..BAND_MAXEXPAND )
end
function PutIdealizeOptionsInBox( dlg )
dlg.sepIdealizer = dialog.AddLabel( "--------------Idealizer Options--------------------" )
dlg.idMaxRange = dialog.AddSlider( "MaxIdealizeRange", IDEALIZE_MAX_SEGLEN, 1, 20, 0 )
dlg.idusecuts = dialog.AddCheckbox( "Allow cuts during idealize", IDEALIZE_USE_CUTS )
dlg.idmusthealcuts = dialog.AddCheckbox( "Forbid unhealed cuts after idealize", IDEALIZE_FORBID_UNHEALED_CUTS )
end
function SetIdealizeOptions( dlg )
IDEALIZE_MAX_SEGLEN = dlg.idMaxRange.value
IDEALIZE_USE_CUTS = dlg.idusecuts.value
IDEALIZE_FORBID_UNHEALED_CUTS = dlg.idmusthealcuts.value
DebugPrint("Idealize max range="..IDEALIZE_MAX_SEGLEN )
if IDEALIZE_USE_CUTS then DebugPrint("Cuts will be made") else DebugPrint("Cuts are forbidden") end
if IDEALIZE_FORBID_UNHEALED_CUTS then DebugPrint("Cuts must heal to be accepted") else DebugPrint("Cuts permitted to survive") end
end
function PutGeneralRebuildOptionsInBox( dlg )
dlg.sepRb = dialog.AddLabel( "---------------Rebuilder Options------------------" )
dlg.rebMaxTries = dialog.AddSlider( "TriesPerStep", REBUILD_TRYCOUNT, 1, 20, 0 )
dlg.rebFailsbeforequit = dialog.AddSlider( "FailsAllowed", REBUILD_FAILSBEFOREQUIT, 10, 100, 0 )
dlg.rebuildMaxRange = dialog.AddSlider( "MaxRebuildRange", REBUILD_MAX_SEGLEN, 3, 20, 0 )
dlg.rebWantIdealize = dialog.AddCheckbox( "Idealize", REBUILD_ADDIDEALIZE )
dlg.rebIdealizeShouldUseCuts = dialog.AddCheckbox( "Idealize uses cuts", REBUILD_USECUTS )
end
function SetGeneralRebuildOptions( dlg )
REBUILD_TRYCOUNT = dlg.rebMaxTries.value
REBUILD_FAILSBEFOREQUIT = dlg.rebFailsbeforequit.value
REBUILD_MAX_SEGLEN = dlg.rebuildMaxRange.value
REBUILD_ADDIDEALIZE = dlg.rebWantIdealize.value
REBUILD_USECUTS = dlg.rebIdealizeShouldUseCuts.value
DebugPrint( "TriesPerStep="..REBUILD_TRYCOUNT )
DebugPrint( "FailsBeforeQuit="..REBUILD_FAILSBEFOREQUIT )
DebugPrint( "MaxRebuildSegmentLength="..REBUILD_MAX_SEGLEN )
if REBUILD_ADDIDEALIZE then DebugPrint( "Rebuilds followed by idealize" ) else DebugPrint( "Rebuild do not idealize" ) end
if REBUILD_USECUTS then DebugPrint( "Rebuild+idealize can use cuts" ) else DebugPrint( "Rebuild+idealize cannot use cuts" ) end
end
function PutSAScaleOptionsInBox( dlg )
dlg.sepScaleSA = dialog.AddLabel( "----------------Scales of Action of Steps---------------------" )
dlg.comment1 = dialog.AddLabel( "Scale actions are 'probabilities' - scaled to sum to 1" )
dlg.scaleLocal = dialog.AddSlider( "S-Local", PROB_LOCAL, 0, 1, 2 )
dlg.scaleMid = dialog.AddSlider( "S-Midsize", PROB_MIDSIZE, 0, 1, 2 )
dlg.scaleTarg = dialog.AddSlider( "S-Targeted", PROB_TARGETED, 0, 1, 2 )
dlg.scaleLarge= dialog.AddSlider( "S-Largescale", PROB_LARGESCALE, 0, 1, 2 )
end
function SetSAScaleOptions( dlg )
local t1 = dlg.scaleLocal.value
local t2 = dlg.scaleMid.value
local t3 = dlg.scaleTarg.value
local t4 = dlg.scaleLarge.value
if t1 + t2 + t3 + t4 == 0 then
t1 = .25
t2 = .25
t3 = .25
t4 = .25
end
PROB_LOCAL = t1 / (t1 + t2 + t3 + t4)
PROB_MIDSIZE = t2 / (t1 + t2 + t3 + t4)
PROB_TARGETED = t3 / (t1 + t2 + t3 + t4)
PROB_LARGESCALE = t4 / (t1 + t2 + t3 + t4)
DebugPrint( "Prob_local="..PROB_LOCAL )
DebugPrint( "Prob_midsize="..PROB_MIDSIZE )
DebugPrint( "Prob_targetd="..PROB_TARGETED )
DebugPrint( "Prob_largescale="..PROB_LARGESCALE )
end
function PutWiggleOptionsInBox( dlg )
PutQuickWiggleOptionsInBox( dlg )
if USE_SLOWWIGGLE then
PutSlowWiggleOptionsInBox( dlg )
end
end
function SetWiggleOptions( dlg )
SetQuickWiggleOptions( dlg )
if USE_SLOWWIGGLE then
SetSlowWiggleOptions( dlg )
end
end
function PutAdvancedSAOptionsInBox( dlg )
PutSAScaleOptionsInBox( dlg )
PutWiggleOptionsInBox( dlg )
--PutBanderOptionsInBox( dlg )
end
function SetAdvancedSAOptions( dlg )
SetSAScaleOptions( dlg )
SetWiggleOptions( dlg )
--SetBanderOptions( dlg )
end
function PutSimpleSAOptionsInBox( dlg )
dlg.stepsper = dialog.AddSlider( "StepsPerRun", SA_STEPSPERRUN, 10, 200, 0 )
dlg.starts = dialog.AddSlider( "RunStarts", SA_NUMSTARTS, 1, 20, 0 )
dlg.samingain = dialog.AddSlider( "MinGainForRun", SA_MINGAIN, 0, 5, 1 )
dlg.quitnogain = dialog.AddSlider( "StopIfNoGain", SA_FAILIFNOGAIN, 1, 200, 0 )
end
function SetSimpleSAOptions( dlg )
SA_STEPSPERRUN = dlg.stepsper.value
SA_NUMSTARTS = dlg.starts.value
SA_MINGAIN = dlg.samingain.value
SA_FAILIFNOGAIN = dlg.quitnogain.value
DebugPrint("SA steps per run="..SA_STEPSPERRUN )
DebugPrint("Step to stop at if no gain="..SA_FAILIFNOGAIN )
DebugPrint("SA run starts="..SA_NUMSTARTS )
DebugPrint("SA halt if gain < "..SA_MINGAIN)
end
function PutMarchWiggleOptionsInBox( dlg )
dlg.sepMW = dialog.AddLabel( "-----------------March Wiggler Options-----------------------" )
dlg.fwiter = dialog.AddSlider( "Wiggle Iters", FINALWIGGLE_ITERS, 2, 20, 0 )
dlg.comment = dialog.AddLabel( "WiggleGain is decimal places: 1, 0.1, .., 0.0001")
dlg.fwgain = dialog.AddSlider( "Wiggle Gain", 3, 0, 4, 0 ) -- there is no good constant to use here!
dlg.mwmingain = dialog.AddSlider( "Remarch Gain", MARCHWIGGLE_MINGAIN, 1, 25, 1 )
dlg.stepstomarch = dialog.AddSlider( "Remarch Steps", MF_STEPSPERRUN, 0, 100, 0 )
dlg.failstoquit = dialog.AddSlider( "Remarch Quit Pt", MF_FAILSBEFORERESTART, 0, 100, 0 )
end
function SetMarchWiggleOptions( dlg )
FINALWIGGLE_ITERS = dlg.fwiter.value
MARCHWIGGLE_MINGAIN = dlg.mwmingain.value
MF_STEPSPERRUN = dlg.stepstomarch.value
MF_FAILSBEFORERESTART = dlg.failstoquit.value
if dlg.fwgain.value == 0 then
FINALRUN_WIGGLE_GAIN = 1
elseif dlg.fwgain.value == 1 then
FINALRUN_WIGGLE_GAIN= 0.1
elseif dlg.fwgain.value == 2 then
FINALRUN_WIGGLE_GAIN = 0.01
elseif dlg.fwgain.value == 3 then
FINALRUN_WIGGLE_GAIN = 0.001
elseif dlg.fwgain.value == 4 then
FINALRUN_WIGGLE_GAIN = 0.0001
end
DebugPrint("FinalWiggle iters="..FINALWIGGLE_ITERS)
DebugPrint("MarchWiggle mingain="..MARCHWIGGLE_MINGAIN)
DebugPrint("FinalWiggle mingain="..FINALRUN_WIGGLE_GAIN)
DebugPrint("Remarch Steps="..MF_STEPSPERRUN)
DebugPrint("Remarch Give-up Point="..MF_FAILSBEFORERESTART)
end
function PutSAROptionsInBox( dlg )
dlg.sepRebSA1= dialog.AddLabel( "----------------Rebuild-SA Options---------------------" )
dlg.rbstarts = dialog.AddSlider( "RunStarts", SA_NUMSTARTS, 1, 20, 0 )
dlg.rbsamingain = dialog.AddSlider( "MinGainForRun", SA_MINGAIN, 0, 10, 1 )
end
function SetSAROptions( dlg )
SA_NUMSTARTS = dlg.rbstarts.value
SA_MINGAIN = dlg.rbsamingain.value
DebugPrint("SA run starts="..SA_NUMSTARTS )
DebugPrint("SA halt if gain < "..SA_MINGAIN)
end
function PutRebuildSAOptionsInBox( dlg, includeCleanup )
PutSAROptionsInBox( dlg )
PutGeneralRebuildOptionsInBox( dlg )
end
function SetRebuildSAOptions( dlg )
SetSAROptions( dlg )
SetGeneralRebuildOptions( dlg )
end
function LetUserChooseParams( )
dlg = dialog.CreateDialog( "Simulated Annealing And More" )
dlg.cmt1 = dialog.AddLabel( "Pick one of following (topmost checked will be run)" )
dlg.wantRebSA = dialog.AddCheckbox( "Low/Auto wiggle: Rebuild+SA", DEFAULT_PICK_AT_START )
dlg.wantJustMW = dialog.AddCheckbox( "Med/High wiggle: MarchWiggle", false )
dlg.cmt2 = dialog.AddLabel( "Other miscellaneous options" )
dlg.wantJustSA = dialog.AddCheckbox( "SA - Simple", false )
dlg.wantSALowCI = dialog.AddCheckbox( "SA - LowCI", false )
dlg.wantSaRebuilder = dialog.AddCheckbox( "SA - Rebuild", false )
dlg.wantSAEarly = dialog.AddCheckbox( "SA - EarlyGame", false )
dlg.wantSALate = dialog.AddCheckbox( "SA - LateGame", false )
dlg.wantSAVeryLate = dialog.AddCheckbox( "SA - VeryLateLowWiggle", false )
if PuzzleHasContactMap then
dlg.wantSAContact = dialog.AddCheckbox( "SA - ContactMap", false )
end
dlg.modLabel = dialog.AddLabel( "------------------Option(s)-----------------------" )
dlg.maxCI = dialog.AddSlider( "MaxCI", SCALE_MAXCI, 0, 1, 2 )
dlg.doslowwiggle = dialog.AddCheckbox( "Perform Fuse in 'misc' actions", USE_SLOWWIGGLE )
if PuzzleHasContactMap then
dlg.cmForceContactBettering = dialog.AddCheckbox( "Contact map tries to improve", CONTACTMAP_TAKEDROPS )
dlg.cmInfo = dialog.AddLabel( "If tries to improve: Max score drop allowed" )
dlg.cmMaxDrop = dialog.AddSlider( " Drop", CONTACTMAP_MAX_DROP, 5, 100, 0 )
end
if PuzzleAllowsMutate then
dlg.cmMutateOrShake = dialog.AddCheckbox( "Mutate instead of shake", true )
dlg.cmAllowPointMutate = dialog.AddCheckbox( "Perform single-aa mutations", ALLOW_SPOT_MUTATION )
end
if PuzzleUsesFilters then
dlg.cmKillFilters = dialog.AddCheckbox( "DisableFilters", KILLING_FILTERS )
end
dlg.preserveCuts = dialog.AddCheckbox( "Preserve user cuts", PRESERVE_USER_CUTS )
if InitialBandCount > 0 then
dlg.preserveBands = dialog.AddCheckbox( "Preserve user bands", true )
end
dlg.moreOptions = dialog.AddCheckbox( "Advanced options", false )
dlg.ok = dialog.AddButton( "OK", 1 )
dlg.cancel = dialog.AddButton( "Cancel", 0 )
if dialog.Show( dlg ) == 0 then return false end
if dlg.wantRebSA.value then OperationChosen = "SARebAndNormal"
elseif dlg.wantJustMW.value then OperationChosen = "MarchWiggle"
elseif dlg.wantJustSA.value then OperationChosen = "SAMidgame"
elseif dlg.wantSALowCI.value then OperationChosen = "SALowCI"
elseif dlg.wantSaRebuilder.value then OperationChosen = "SARebuild"
elseif dlg.wantSAEarly.value then OperationChosen = "SAEarly"
elseif dlg.wantSALate.value then OperationChosen = "SALate"
elseif dlg.wantSAVeryLate.value then OperationChosen = "SAVeryLate"
elseif PuzzleHasContactMap and dlg.wantSAContact.value then OperationChosen = "SAContact"
else return false -- something went wrong...
end
DEFAULT_PICK_AT_START = false
SCALE_MAXCI = dlg.maxCI.value
DebugPrint( "Using MaxCI="..SCALE_MAXCI )
if PuzzleHasContactMap then
CONTACTMAP_TAKEDROPS = dlg.cmForceContactBettering.value
if CONTACTMAP_TAKEDROPS then
print( "Score drops of ".. CONTACTMAP_MAX_DROP .. " accepted if ContactMap improves" )
else
print( "No score drops permitted" )
end
end
if PuzzleAllowsMutate then
MUTATE_NOT_SHAKE = dlg.cmMutateOrShake.value
ALLOW_SPOT_MUTATION = dlg.cmAllowPointMutate.value
end
if PuzzleUsesFilters then
KILLING_FILTERS = dlg.cmKillFilters.value
end
USE_SLOWWIGGLE = dlg.doslowwiggle.value
DebugPrint( "Cleanup wiggle after? ".. BoolStr( USE_SLOWWIGGLE) )
if InitialBandCount > 0 then
BANDS_PRESERVE_USER = dlg.preserveBands.value
if not BANDS_PRESERVE_USER then
InitialBandCount = 0
DelBands( )
end
DebugPrint( "Preserve user bands? ".. BoolStr( PRESERVE_USER_BANDS ) )
end
PRESERVE_USER_CUTS = dlg.preserveCuts.value
if PRESERVE_USER_CUTS then
IDEALIZE_USE_CUTS = false
REBUILD_USECUTS = false
PROB_SLOWWIGGLE_CUTFUSE = 0.00
PROB_SLOWWIGGLE_LOCAL = 0.20
PROB_SLOWWIGGLE_BLUEFUSE = 0.80
end
DebugPrint( "Preserve user cuts? ".. BoolStr( PRESERVE_USER_CUTS ) )
if not dlg.moreOptions.value then return true end
-- user wants to keep talking to dialog boxes...
if OperationChosen == "SARebuild" then
dlg = dialog.CreateDialog( "Rebuild-Simulated Annealing Parameters" )
PutRebuildSAOptionsInBox( dlg, true )
dlg.RbWigglerPars= dialog.AddCheckbox( "Set Wiggle Options", false )
dlg.ok = dialog.AddButton( "OK", 1 )
dlg.cancel = dialog.AddButton( "Cancel", 0 )
if dialog.Show( dlg ) == 0 then return false end
SetRebuildSAOptions( dlg, true )
if dlg.RbWigglerPars.value then
dlg = dialog.CreateDialog( "Rebuilder Wiggle Options" )
PutWiggleOptionsInBox( dlg )
dlg.ok = dialog.AddButton( "OK", 1 )
dlg.cancel = dialog.AddButton( "Cancel", 0 )
if dialog.Show( dlg ) == 0 then return false end
SetWiggleOptions( dlg )
end
end
if OperationChosen == "SAEarly" or OperationChosen == "SALate" or
OperationChosen == "SAContact" or OperationChosen == "SALowCI"
then
dlg = dialog.CreateDialog( "Simulated Annealing Special-purpose Parameters" )
PutSimpleSAOptionsInBox( dlg )
dlg.SAAdvancedPars= dialog.AddCheckbox( "Advanced parameters", false )
dlg.ok = dialog.AddButton( "OK", 1 )
dlg.cancel = dialog.AddButton( "Cancel", 0 )
if dialog.Show( dlg ) == 0 then return false end
SetSimpleSAOptions( dlg )
if dlg.SAAdvancedPars.value then
dlg = dialog.CreateDialog( "Advanced SA Special-purpose Options" )
PutWiggleOptionsInBox( dlg )
--PutBanderOptionsInBox( dlg )
dlg.IdRAdvancedPars= dialog.AddCheckbox( "Idealize/Rebuild obscure params", false )
dlg.ok = dialog.AddButton( "OK", 1 )
dlg.cancel = dialog.AddButton( "Cancel", 0 )
if dialog.Show( dlg ) == 0 then return false end
SetWiggleOptions( dlg )
--SetBanderOptions( dlg )
if dlg.IdRAdvancedPars.value then
dlg = dialog.CreateDialog( "Idealize/Rebuild Options" )
PutIdealizeOptionsInBox( dlg )
PutGeneralRebuildOptionsInBox( dlg )
dlg.ok = dialog.AddButton( "OK", 1 )
dlg.cancel = dialog.AddButton( "Cancel", 0 )
if dialog.Show( dlg ) == 0 then return false end
SetIdealizeOptions( dlg )
SetGeneralRebuildOptions( dlg )
end
end
end
if OperationChosen == "SAMidgame" then
dlg = dialog.CreateDialog( "Simulated Annealing Parameters" )
PutSimpleSAOptionsInBox( dlg )
dlg.SAAdvancedPars= dialog.AddCheckbox( "Advanced parameters", false )
dlg.ok = dialog.AddButton( "OK", 1 )
dlg.cancel = dialog.AddButton( "Cancel", 0 )
if dialog.Show( dlg ) == 0 then return false end
SetSimpleSAOptions( dlg )
if dlg.SAAdvancedPars.value then
dlg = dialog.CreateDialog( "Advanced SA Parameters" )
PutAdvancedSAOptionsInBox( dlg )
dlg.IdRAdvancedPars= dialog.AddCheckbox( "Idealize/Rebuild obscure params", false )
dlg.ok = dialog.AddButton( "OK", 1 )
dlg.cancel = dialog.AddButton( "Cancel", 0 )
if dialog.Show( dlg ) == 0 then return false end
SetAdvancedSAOptions( dlg )
if dlg.IdRAdvancedPars.value then
dlg = dialog.CreateDialog( "Idealize/Rebuild Options" )
PutIdealizeOptionsInBox( dlg )
PutGeneralRebuildOptionsInBox( dlg )
dlg.ok = dialog.AddButton( "OK", 1 )
dlg.cancel = dialog.AddButton( "Cancel", 0 )
if dialog.Show( dlg ) == 0 then return false end
SetIdealizeOptions( dlg )
SetGeneralRebuildOptions( dlg )
end
end
end
if OperationChosen == "MarchWiggle" then
dlg = dialog.CreateDialog( "March-Wiggle Parameters" )
PutMarchWiggleOptionsInBox( dlg )
dlg.ok = dialog.AddButton( "OK", 1 )
dlg.cancel = dialog.AddButton( "Cancel", 0 )
if dialog.Show( dlg ) == 0 then return false end
SetMarchWiggleOptions( dlg )
end
if OperationChosen == "SARebAndNormal" then
dlg = dialog.CreateDialog( "Rebuild+SA Parameters" )
PutSimpleSAOptionsInBox( dlg )
PutSAROptionsInBox( dlg )
dlg.SAAdvancedPars = dialog.AddCheckbox( "Advanced parameters", false )
dlg.ok = dialog.AddButton( "OK", 1 )
dlg.cancel = dialog.AddButton( "Cancel", 0 )
if dialog.Show( dlg ) == 0 then return false end
SetSimpleSAOptions( dlg )
SetSAROptions( dlg, false )
if dlg.SAAdvancedPars.value then
dlg = dialog.CreateDialog( "Advanced SA Parameters" )
PutAdvancedSAOptionsInBox( dlg )
dlg.IdRAdvancedPars= dialog.AddCheckbox( "Idealize/Rebuild obscure params", false )
dlg.ok = dialog.AddButton( "OK", 1 )
dlg.cancel = dialog.AddButton( "Cancel", 0 )
if dialog.Show( dlg ) == 0 then return false end
SetAdvancedSAOptions( dlg )
if dlg.IdRAdvancedPars.value then
dlg = dialog.CreateDialog( "Idealize/Rebuild Options" )
PutIdealizeOptionsInBox( dlg )
PutGeneralRebuildOptionsInBox( dlg )
dlg.ok = dialog.AddButton( "OK", 1 )
dlg.cancel = dialog.AddButton( "Cancel", 0 )
if dialog.Show( dlg ) == 0 then return false end
SetIdealizeOptions( dlg )
SetGeneralRebuildOptions( dlg )
end
end
end
return true
end
----------------------------------------------------------------
--
---------------- BOILERPLATE ---------------------
--
----------------------------------------------------------------
----------------------------------------------------------------
-- SIMPLE HELPER FUNCTIONS
----------------------------------------------------------------
function DebugPrint( str )
if DEBUGRUN then print( str ) end
end
function BoolStr( bval )
if bval then return "true" end
return "false"
end
function TrimNum( val )
return val - val % 0.001
end
----------------------------------------------------------------
-- SLOW FILTER FUNCTIONS
----------------------------------------------------------------
function TurnOnSlowFilters( )
if behavior.GetSlowFiltersDisabled( ) then
--print("slow filters are now ON")
behavior.SetSlowFiltersDisabled( false )
end
end
function TurnOffSlowFilters( )
if not behavior.GetSlowFiltersDisabled( ) then
--print("slow filters are now OFF")
behavior.SetSlowFiltersDisabled( true )
end
end
function ResetSlowFilterState( )
behavior.SetSlowFiltersDisabled( IsSlowFilterDisabled )
end
function CheckForSlowFilters( )
TurnOffSlowFilters( )
local retval = not current.AreConditionsMet()
ResetSlowFilterState( )
return retval
end
----------------------------------------------------------------
-- BASIC SCORE FUNCTIONS
----------------------------------------------------------------
-- Not for "external" use - call getScore. This could change if customers want
-- something besides current or recentbest.
function internalGetScore( wantRB, useFilter )
if wantRB == nil then wantRB = false end
if useFilter and KILLING_FILTERS then TurnOnSlowFilters( ) end
local s=0.0
if not USENORMALSCORE then
if wantRB then s = recentbest.GetEnergyScore( )
else s=current.GetEnergyScore( )
end
else
if wantRB then s = recentbest.GetScore( )
else s=current.GetScore( )
end
end
if useFilter and KILLING_FILTERS then TurnOffSlowFilters( ) end
return s
end
function getScore( )
return internalGetScore( false, true )
end
function getQuickScore( )
return internalGetScore( false, false )
end
function getRBScore( )
return internalGetScore( true, true )
end
function SaveBest( forceBest ) -- most callers will leave as nil.
local score = getScore( )
if forceBest or score >= CurrentBestScore then
save.Quicksave( QS_Best ) -- use "score >=" because same-with-unfrozen/unbanded is better
CurrentBestScore = score
end
if PuzzleHasContactMap then
cmScore = GetContactScore( )
if forceBest or
(cmScore >= CurrentBestCmTotal and CONTACTMAP_TAKEDROPS and
score > CurrentBestScore - CONTACTMAP_MAX_DROP)
then
save.Quicksave( QS_CmBest )
CurrentBestCmScore = score
CurrentBestCmTotal = cmScore
end
if CONTACTMAP_TAKEDROPS and CurrentBestScore ~= CurrentBestCmScore then
DebugPrint(" In Best("..QS_Best.."): score="..CurrentBestScore )
DebugPrint(" In CmBest("..QS_CmBest.."): score="..CurrentBestCmScore.." ("..CurrentBestCmTotal..")" )
end
end
end
function LoadBest( )
if PuzzleHasContactMap and CONTACTMAP_TAKEDROPS and
CurrentBestCmScore >= CurrentBestScore - CONTACTMAP_MAX_DROP
then
save.Quickload( QS_CmBest )
else
save.Quickload( QS_Best )
end
end
function GetBestScore( )
if PuzzleHasContactMap and CONTACTMAP_ALLOWDROP and
CurrentBestCmScore >= CurrentBestScore - CONTACTMAP_MAX_DROP
then
return CurrentBestCmScore
else
return CurrentBestScore
end
end
----------------------------------------------------------------
-- MATHY FUNCTIONS (random, matrix, primes, sorting)
----------------------------------------------------------------
function seedRandom( )
seed=os.time( )/math.abs( getQuickScore( ) )
seed=seed%0.001
seed=1/seed
while seed<10000000 do seed=seed*1000 end
seed=seed-seed%1
DebugPrint( "Seed is: "..seed )
math.randomseed( seed )
-- throw away a couple of randoms
math.random( )
math.random( )
end
function random( n1,n2, forceFloat ) --random function returns int or float depends on input vars
if forceFloat == nil then forceFloat = false end
if n1==nil then
return math.random( )
else
if n2==nil then
if n1 == 0 then return 0 end -- a random number between 0 and 0 is 0
if n1%1==0 then -- can't test for "forceFloat", so caller must beware
return math.random( n1) --integer
else
return math.random( ) * n1 --float
end
else
if n1%1==0 and n2%1==0 and not forceFloat then
return math.random( n1, n2 ) --integer between
else
return math.random( ) * (n2 - n1) + n1 --float between
end
end
end
end
function randomDice( ctDice, minValPerDie, maxValPerDie ) -- distro that prefers "middle" values
total = 0
for i=1, ctDice do
total = total + random( minValPerDie, maxValPerDie )
end
return total
end
function randomThetaPhi( )
return math.acos( random( -1.0, 1.0, true ) ), random( 2 * math.pi )
end
function randomizeIndexList( idxList )
for i=1, #idxList do
j = random( #idxList )
if j ~= i then
idxList[i], idxList[j] = idxList[j], idxList[i]
end
end
end
function randomSeg( )
return random( segCt )
end
function randomUnlockedSeg( )
if not PuzzleHasLockedSegs then
return randomSeg( )
end
return NonLockedSegList[ random( #NonLockedSegList ) ]
end
function randomAASeg( whichAA )
local segs = {}
for i=1, #NonLockedSegList do
if structure.GetAminoAcid( i ) == whichAA then
segs[ #segs + 1 ] = i
end
end
if #segs == 0 then return 0 end
return segs[ random( #segs ) ]
end
function randomMovableSeg( )
for i=1, 20 do
local seg = randomUnlockedSeg( )
local bb, sc = freeze.IsFrozen( seg )
if not bb then return seg end
end
-- if we are here, then we had a hard time finding a nonfrozen seg.
-- make an array and search the hard way.
local movables = {}
for i=1, #NonLockedSegList do
local bb, sc = freeze.IsFrozen( seg )
if not bb then movables[ #movables + 1 ] = i end
end
if #movables == 0 then return 1 end -- nothing we can do
return movables[ random( #movables ) ]
end
function randomMovableFarSeg( segOrigin, minSep, minDist, maxDist )
local segList = {}
for e=1, segCt do
if IsMovableSeg( e ) and (e <= segOrigin - minSep or e >= segOrigin + minSep ) then
dist = structure.GetDistance( segOrigin, e )
if dist >= minDist and dist <= maxDist then segList[ #segList + 1 ] = e end
end
end
if #segList >= 1 then return segList[ random( #segList ) ]
else return 0
end
end
function randomLowScoringSeg( ctIndicesToChooseAmong, subscore )
local idxList = {}
getWorstScoringAAs( idxList, ctIndicesToChooseAmong, subscore )
return idxList[ random( #idxList ) ]
end
function randomContactableSeg( )
return HasContactMapSegList[ random( #HasContactMapSegList ) ]
end
function randomContactSeg( segIn, heatThreshold )
local segOpts = {}
for i = 1, segCt do
if i < segIn - 1 or i > segIn + 1 then
if contactmap.GetHeat( segIn, i ) >= heatThreshold then
segOpts[ #segOpts + 1] = i
end
end
end
if #segOpts == 0 then return 0 end
return segOpts[ random( #segOpts ) ]
end
function RandomPrimeNotMoreThan( max )
local primes = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101}
lastIdx = #primes
for i=1, #primes do
if primes[ i ] > max then break end
lastIdx = i
end
return primes[ math.random( lastIdx ) ]
end
function Coprime( n )
-- find the highest prime < 70% of "n" that is coprime with "n"
local primes = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101}
for i = #primes, 1, -1 do
if primes[ i ] < 0.70*n and n % primes[ i ] ~= 0 then
return primes[ i ]
end
end
return 1
end
function GetEuclideanDistanceVector( scmat1, scmat2, vecOut )
score = 0.0
for j=1, #scmat1[1] do
vecOut[j] = 0.0
end
for i=1, segCt do
for j=1, #scmat1[i] do
vecOut[j] = vecOut[j] + (scmat2[i][j] - scmat1[i][j]) * (scmat2[i][j] - scmat1[i][j])
end
end
for j=1, #vecOut do
vecOut[j] = math.sqrt( vecOut[j] )
end
end
function sortAllByScore(segs, scores)
table.sort(segs, function(n1,n2) return scores[n1] < scores[n2] end)
end
----------------------------------------------------------------
-- SCORE-USING FUNCTIONS
----------------------------------------------------------------
function ConstructScoreMatrix( scmat )
-- TODO: Get the list of "active" subscores from puzzle info; use that info here.
for i=1, segCt do
scmat[i] = {}
scmat[i][1] = current.GetSegmentEnergySubscore( i, "backbone" )
scmat[i][2] = current.GetSegmentEnergySubscore( i, "sidechain" )
scmat[i][3] = current.GetSegmentEnergySubscore( i, "clashing" )
scmat[i][4] = current.GetSegmentEnergySubscore( i, "packing" )
scmat[i][5] = current.GetSegmentEnergySubscore( i, "density" )
scmat[i][6] = current.GetSegmentEnergySubscore( i, "hiding" )
scmat[i][7] = current.GetSegmentEnergySubscore( i, "bonding" )
scmat[i][8] = current.GetSegmentEnergySubscore( i, "disulfides" )
scmat[i][9] = current.GetSegmentEnergySubscore( i, "ideality" )
scmat[i][10] = current.GetSegmentEnergySubscore( i, "pairwise" )
scmat[i][11] = current.GetSegmentEnergySubscore( i, "other" )
end
end
function IsNeighborDifferentFromOld( oldScoreMat, threshold )
local newScoreMat = {}
local scoreVector = {}
ConstructScoreMatrix( newScoreMat )
GetEuclideanDistanceVector( oldScoreMat, newScoreMat, scoreVector )
for i=1, #scoreVector do
if scoreVector[i] > threshold then return true end
end
return false
end
function IsProteinChanged( oldQuickScore, oldScoreMat, threshold )
if (not KILLING_FILTERS) and (not current.AreConditionsMet( )) then return false end
local currScore = getQuickScore( )
if currScore > oldQuickScore then return true end
return IsNeighborDifferentFromOld( oldScoreMat, threshold )
end
-- returns all aas in order of score (if subscore is nil then does GetSegmentEnergyScore)
function getWorstScoringAAs( idxList, maxWanted, subscore )
local scoreList = {}
local idxes = {}
for i=1, segCt do
idxes[i] = i
end
for i=1, segCt do
if subscore == nil then
scoreList[i] = current.GetSegmentEnergyScore(i)
else
scoreList[i] = current.GetSegmentEnergySubscore( i, subscore )
end
end
sortAllByScore( idxes, scoreList )
for i = 1, math.min( maxWanted, segCt ) do
idxList[i] = idxes[i]
end
end
function getBestScoringAAs( idxList, maxWanted, subscore )
local scoreList = {}
local idxes = {}
for i=1, segCt do
idxes[i] = i
end
for i=1, segCt do
if subscore == nil then
scoreList[i] = current.GetSegmentEnergyScore(i)
else
scoreList[i] = current.GetSegmentEnergySubscore( i, subscore )
end
end
sortAllByScore( idxes, scoreList )
for i = 1, math.min( maxWanted, segCt ) do
idxList[i] = idxes[segCt - i + 1]
end
end
function isBadScorer(idx, mustBeAtLeastThisBad)
local idxList = {}
getWorstScoringAAs( idxList, mustBeAtLeastThisBad)
for i=1, #idxList do
if idxList[i] == idx then return true end
end
return false
end
function SortIdxListBySubscore( idxList, subscoreName )
local subscoreList = {}
for i=1, #idxList do
subscoreList[ i ] = current.GetSegmentEnergySubscore( idxList[ i ], subscoreName )
end
sortAllByScore( idxList, subscoreList )
end
function GetWorstScoringRegions( idxList, len )
scoreList = { }
for i=1, segCt - len + 1 do
scoreList[ i ] = 0.0
idxList[ i ] = i
for j = 0, len - 1 do
scoreList[ i ] = scoreList[ i ] + current.GetSegmentEnergyScore( i + j )
end
end
sortAllByScore( idxList, scoreList )
end
----------------------------------------------------------------
-- DISTANCE FUNCTIONS
----------------------------------------------------------------
function computeDistanceSums(distList)
for i=1, segCt do
distList[i] = 0.0
end
for i=1, segCt-1 do
for j=i+1, segCt do
distList[i] = distList[i] + structure.GetDistance( i, j ) / segCt
distList[j] = distList[j] + structure.GetDistance( i, j ) / segCt
end
end
end
function getCentralAAs( idxList )
local distList = {}
for i=1, segCt do
idxList[i] = i
end
computeDistanceSums( distList )
sortAllByScore( idxList, distList )
end
-- returns all hydrophobics in order of "centralness"
function getCentralHydrophobics(idxList, ctWanted)
local idxListInternal = {}
getCentralAAs(idxListInternal)
for i=1, #idxListInternal do
if structure.IsHydrophobic( idxListInternal[ i ] ) then
idxList[ #idxList + 1 ] = idxListInternal[ i ]
if #idxList == ctWanted then return end
end
end
end
function isCentralHydrophobic( idx, mustBeAtLeastThisGood )
local idxList = {}
getCentralHydrophobics( idxList, mustBeAtLeastThisGood )
for i=1, #idxList do
if idxList[i] == idx then return true end
end
return false
end
function getSegsWithinSphere( idxList, centerIdx, radius, includeCenter )
if includeCenter then
idxList[ #idxList + 1 ] = centerIdx
end
for i=1, segCt do
if i ~= centerIdx and structure.GetDistance( centerIdx, i ) <= radius then
idxList[ #idxList + 1 ] = i
end
end
end
function getSegsOutsideSphere( idxList, centerIdx, radius )
for i=1, segCt do
if i ~= centerIdx and structure.GetDistance( centerIdx, i ) > radius then
idxList[ #idxList + 1 ] = i
end
end
end
function getCMSegsWithinSphere( idxList, centerIdx, radius, includeCenter, heatThreshold )
if includeCenter then
idxList[ #idxList + 1 ] = centerIdx
end
for i=1, segCt do
if i ~= centerIdx and structure.GetDistance( centerIdx, i ) <= radius and
contactmap.GetHeat( centerIdx, i ) >= heatThreshold
then
idxList[ #idxList + 1 ] = i
end
end
end
function getCMSegsOutsideSphere( idxList, centerIdx, radius, heatThreshold )
for i=1, segCt do
if i ~= centerIdx and structure.GetDistance( centerIdx, i ) > radius and
contactmap.GetHeat( centerIdx, i ) >= heatThreshold
then
idxList[ #idxList + 1 ] = i
end
end
end
function getSegsWithinRangeOfTube( idxList, startSeg, endSeg, radius )
-- if a seg is "near" to one of the ones in our [startSeg, endSeg] range, then we want it
for i=1, segCt do
for j=startSeg, endSeg do
if structure.GetDistance( i, j ) < radius then
idxList[ #idxList + 1] = i
break
end
end -- for j
end -- for i
end
function IsLocalMinimum( segCenter, segCheck)
if segCheck == segCenter then return false end -- exclude selfies
ckDist = structure.GetDistance(segCenter, segCheck)
if segCheck > 1 then
ckDist1 = structure.GetDistance( segCenter, segCheck - 1 )
if ckDist1 < ckDist then return false end
if segCheck > 2 then
ckDist2 = structure.GetDistance( segCenter, segCheck - 2 )
if ckDist2 < ckDist then return false end
end
end
if segCheck < segCt then
ckDist1 = structure.GetDistance( segCenter, segCheck + 1 )
if ckDist1 < ckDist then return false end
if segCheck < segCt - 1 then
ckDist2 = structure.GetDistance( segCenter, segCheck + 2 )
if ckDist2 < ckDist then return false end
end
end
return true
end
function IsLocalMaximum( segCenter, segCheck)
if segCheck == segCenter then return false end -- exclude selfies
ckDist = structure.GetDistance(segCenter, segCheck)
if segCheck > 1 then
ckDist1 = structure.GetDistance( segCenter, segCheck - 1 )
if ckDist1 > ckDist then return false end
if segCheck > 2 then
ckDist2 = structure.GetDistance( segCenter, segCheck - 2 )
if ckDist2 > ckDist then return false end
end
end
if segCheck < segCt then
ckDist1 = structure.GetDistance( segCenter, segCheck + 1 )
if ckDist1 > ckDist then return false end
if segCheck < segCt - 1 then
ckDist2 = structure.GetDistance( segCenter, segCheck + 2 )
if ckDist2 > ckDist then return false end
end
end
return true
end
----------------------------------------------------------------
-- ACTION STATISTICS
----------------------------------------------------------------
function WhereIsStringInArray( array, str )
for i=1, #array do if str == array[ i ] then return i end end
return 0
end
function AddActionToStats( scoreDelta, timeDelta )
if WhereIsStringInArray( algNameList, algName ) == 0 then
algNameList[ #algNameList + 1] = algName
end
if algStats[ algName ] == nil then
algStats[ algName ] = {
posScoreDelta = 0.0,
scoreDelta = 0.0,
runTime = 0,
callCount = 0,
gainCount = 0,
failCount = 0,
}
end
if scoreDelta > 0.0 then
algStats[ algName ].posScoreDelta = algStats[ algName ].posScoreDelta + scoreDelta
algStats[ algName ].gainCount = algStats[ algName ].gainCount + 1
end
algStats[ algName ].scoreDelta = algStats[ algName ].scoreDelta + scoreDelta
algStats[ algName ].runTime = algStats[ algName ].runTime + timeDelta
algStats[ algName ].callCount = algStats[ algName ].callCount + 1
end
function AddFailToStats( )
if WhereIsStringInArray( algNameList, algName ) == 0 then
algNameList[ #algNameList + 1] = algName
end
if algStats[ algName ] == nil then
algStats[ algName ] = {
posScoreDelta = 0.0,
scoreDelta = 0.0,
runTime = 0,
callCount = 0,
gainCount = 0,
failCount = 1,
}
return
end
algStats[ algName ].failCount = algStats[ algName ].failCount + 1
end
function ChooseAlgorithm( algList )
-- we dither runs and gains by 1 partly to avoid divide-by-zero, and
-- partly to encourage bringing in not-yet-used algorithms.
local scaleValue = 0.0
local funcValues = {}
for i = 1, #algList do
local runs = 0
local gains = 0
if algStats[ algList[ i ] ] == nil then
runs = 1
gains = 1
else
-- failCount/2 is a bit artificial, but failed actions should count against an algorithm too.
runs = 1 + algStats[ algList[ i ] ].callCount + math.floor( algStats[ algList[ i ] ].failCount / 2 )
gains = 1 + algStats[ algList[ i ] ].gainCount
end
funcValues[ i ] = gains / runs -- finding value of this item
scaleValue = scaleValue + funcValues[ i ] -- finding total size of bucket
end
local which = random( )
local sum = 0.0
for i = 1, #funcValues do
sum = sum + ( funcValues[ i ] / scaleValue )
if which < sum then
return algList[ i ]
end
end
return algList[ #algList ]
end
function ReportStats( )
print("Action stats:")
for i = 1, #algNameList do
local t = algNameList[ i ]
if algStats[ t ] ~= nil and algStats[ t ].callCount > 0 then
print( " " .. t .. ": scoreDel=" .. TrimNum( algStats[ t ].scoreDelta ) ..
" posScore=" .. TrimNum( algStats[ t ].posScoreDelta ) ..
" runTime=" .. algStats[ t ].runTime ..
" runs=" .. algStats[ t ].callCount ..
" gains=" .. algStats[ t ].gainCount ..
" fails=" .. algStats[ t ].failCount )
end
end
end
function ResetStats( )
for i = 1, #algNameList do
local t = algNameList[ i ]
if algStats[ t ] ~= nil then
algStats[ t ].scoreDelta = 0
algStats[ t ].posScoreDelta = 0
algStats[ t ].runTime = 0
algStats[ t ].callCount = 0
algStats[ t ].gainCount = 0
algStats[ t ].failCount = 0
end
end
end
----------------------------------------------------------------
-- SIDECHAIN AND ATOM FUNCTIONS
----------------------------------------------------------------
aaCount = 20
aaPhilCount = 9
aaPhobCount = 11
function AA(idx)
-- hydrophilics
if idx == 1 then return "r"
elseif idx == 2 then return "s"
elseif idx == 3 then return "t"
elseif idx == 4 then return "n"
elseif idx == 5 then return "d"
elseif idx == 6 then return "q"
elseif idx == 7 then return "e"
elseif idx == 8 then return "h"
elseif idx == 9 then return "k"
-- hydrophobics
elseif idx == 10 then return "g"
elseif idx == 11 then return "a"
elseif idx == 12 then return "c"
elseif idx == 13 then return "v"
elseif idx == 14 then return "l"
elseif idx == 15 then return "i"
elseif idx == 16 then return "m"
elseif idx == 17 then return "p"
elseif idx == 18 then return "f"
elseif idx == 19 then return "y"
else return "w" end -- idx == 20
end
function AAphilic(idx)
-- hydrophilics
if idx == 1 then return "r"
elseif idx == 2 then return "s"
elseif idx == 3 then return "t"
elseif idx == 4 then return "n"
elseif idx == 5 then return "d"
elseif idx == 6 then return "q"
elseif idx == 7 then return "e"
elseif idx == 8 then return "h"
else return "k" -- idx == 9
end
end
function AAphobic(idx)
-- hydrophobics
if idx == 1 then return "g"
elseif idx == 2 then return "a"
elseif idx == 3 then return "c"
elseif idx == 4 then return "v"
elseif idx == 5 then return "l"
elseif idx == 6 then return "i"
elseif idx == 7 then return "m"
elseif idx == 8 then return "p"
elseif idx == 9 then return "f"
elseif idx == 10 then return "y"
else return "w" -- idx == 11
end
end
-- for branched aas, simply picks one (longer, one with donor/acceptor tip, or if no difference then either)
function GetAtomOfTip( aa )
if aa == "a" then return 10
elseif aa == "c" then return 10
elseif aa == "d" then return 8
elseif aa == "e" then return 9
elseif aa == "f" then return 20
elseif aa == "g" then return 0 -- glycine has no tip. just use a backbone atom
elseif aa == "h" then return 17
elseif aa == "i" then return 18
elseif aa == "k" then return 9
elseif aa == "l" then return 16
elseif aa == "m" then return 17
elseif aa == "n" then return 14
elseif aa == "p" then return 13
elseif aa == "q" then return 9
elseif aa == "r" then return 22
elseif aa == "s" then return 11
elseif aa == "t" then return 6
elseif aa == "v" then return 13
elseif aa == "w" then return 24
elseif aa == "y" then return 12
else return 0
end
end
function GetTipAtomOfSeg( idx )
return GetAtomOfTip( structure.GetAminoAcid( idx ))
end
function PickAtomNumberForBand( idx )
if not BAND_TO_SIDECHAINS then return CENTER_CARBON end
local r = random( 1, 3 ) -- consider adjusting probability?
if r == 1 then
return BETA_CARBON
elseif r == 2 then
return GetTipAtomOfSeg( idx )
else
return CENTER_CARBON
end
end
----------------------------------------------------------------
-- MUTATION-RELATED FUNCTIONS
----------------------------------------------------------------
function checkAla( )
local ala=0
for n = 1, segCt do
segType = structure.GetAminoAcid(n)
if segType == 'a' or segType == 'g' then
ala=ala+1
end
end
return ala == segCt
end
function IsPuzzleMutable( )
for i=1, segCt do
if structure.IsMutable(i) then return true end
end
return false
end
function IsNoMutate( idx )
if not ALLOW_SPOT_MUTATION then return true end
if not PuzzleAllowsMutate then return true end
if not structure.IsMutable(idx) then return true end
for i=1,#NOMUTATELIST do
if idx == NOMUTATELIST[i] then return true end
end
return false
end
----------------------------------------------------------------
-- LOCKED/FROZEN FUNCTIONS
----------------------------------------------------------------
function FindAllMovableSegs( )
for i=1, segCt do
if structure.IsLocked( i ) then
PuzzleHasLockedSegs = true
else
NonLockedSegList[ #NonLockedSegList + 1] = i
end
end
if PuzzleHasLockedSegs then
for i=1, segCt do
if IsUnlockedSeg( i ) then
MinUnlockedSeg = i
break
end
end
for i=segCt, 1, -1 do
if IsUnlockedSeg( i ) then
MaxUnlockedSeg = i
break
end
end
end
return false
end
function IsMovableSeg( seg1 )
if seg1 == nil or seg1 < 1 or seg1 > segCt then return false end
local BB, SC = freeze.IsFrozen( seg1 )
local sl = structure.IsLocked( seg1 )
return not (BB or SC or sl)
end
function IsUnlockedSeg( seg )
return not structure.IsLocked( seg )
end
function IsAllMovableRange(seg1, seg2)
if seg1 == nil or seg2 == nil then return false end
if seg1 > seg2 then seg1, seg2 = seg2, seg1 end
if seg1 < 1 or seg2 > segCt then return false end
for i=seg1, seg2 do
if not IsMovableSeg( i ) then return false end
end
return true
end
function SelectAllMovable( )
for i=1, segCt do
if IsMovableSeg( i ) then selection.Select( i ) end
end
end
function ResetFrozenness( )
freeze.UnfreezeAll( )
for i=1, segCt do
bFroz, sFroz = InitialFrozenTable[ i ]
if bFroz or sFroz then
freeze.Freeze( i, bFroz, sFroz )
end
end
ChangedFrozenness = false
end
function SaveFrozenness( )
for i=1, segCt do -- save "frozenness"
local bFroz, sFroz = freeze.IsFrozen( i )
InitialFrozenTable[ i ] = bFroz, sFroz
end
end
----------------------------------------------------------------
-- CONTACT MAP FUNCTIONS
----------------------------------------------------------------
function CheckForContactMap( )
PuzzleHasContactMap = false
local saveval = 0.0
for i=1, segCt-1 do
for j=i+1, segCt do
val = contactmap.GetHeat( i, j )
if saveval ~= 0.0 and val ~= saveval then
PuzzleHasContactMap = true
ContactMapScore = GetContactScore( )
return -- all we wanted to know was whether scores exist
end
if saveval == 0.0 then saveval = val end
end
end
return
end
function SegHasContactData( segIn, heatThreshold )
for i = 1, segCt do
if i < segIn - 1 or i > segIn + 1 then
if contactmap.GetHeat( segIn, i ) >= heatThreshold then return true end
end
end
return false
end
function InitializeContactMapSegList( heatThreshold )
HasContactMapSegList = {}
for i = 1, segCt do
if SegHasContactData( i, heatThreshold ) then
HasContactMapSegList[ #HasContactMapSegList + 1 ] = i
end
end
end
function GetContactScore( )
if not PuzzleHasContactMap then return 0 end
local sum = 0.0
local segCt = structure.GetCount( )
for i = 1,segCt-1 do
for j = i + 1, segCt do
if contactmap.IsContact( i, j ) then sum = sum + contactmap.GetHeat( i, j ) end
end
end
return sum
end
function ActionDamagedContactMap( )
if PuzzleHasContactMap then
local sc = GetContactScore( )
if sc < ContactMapScore then return true end
ContactMapScore = sc -- now we have a "better" score, so keep that one
end
return false
end
----------------------------------------------------------------
-- SECONDARY STRUCTURES
----------------------------------------------------------------
function getHelices( )
inside = false
for i=MinUnlockedSeg, MaxUnlockedSeg do
if ( IsUnlockedSeg( i ) and structure.GetSecondaryStructure(i) == "H" ) then
if ( inside == false ) then
inside = true
helixStarts[ #helixStarts + 1] = i
end
elseif ( IsUnlockedSeg( i ) and inside == true ) then
inside = false
helixEnds[ #helixEnds + 1] = i - 1
end -- if ( "H" ) elseif ( within )
end -- for (segCt)
-- deal with "last seg is helix"
if ( inside == true ) then
helixEnds[ #helixEnds + 1] = MaxUnlockedSeg
end
end
function getSheets( )
inside = false
for i=MinUnlockedSeg, MaxUnlockedSeg do
if ( IsUnlockedSeg( i ) and structure.GetSecondaryStructure(i) == "E" ) then
if ( inside == false ) then
inside = true
sheetStarts[ #sheetStarts + 1 ] = i
end
elseif (IsUnlockedSeg( i ) and inside == true ) then
inside = false
sheetEnds[ #sheetEnds + 1 ] = i - 1
end -- if/else 'E'
end -- for (segCt)
-- deal with "last seg is sheet"
if ( inside == true ) then
sheetEnds[ #sheetEnds + 1 ] = MaxUnlockedSeg
end
end
function getLoops( )
inside = false
for i=MinUnlockedSeg, MaxUnlockedSeg do
if ( IsUnlockedSeg( i ) and structure.GetSecondaryStructure(i) == "L" ) then
if ( inside == false ) then
inside = true
loopStarts[ #loopStarts + 1 ] = i
end
elseif ( IsUnlockedSeg( i ) and inside == true ) then
inside = false
loopEnds[ #loopEnds + 1 ] = i - 1
end -- if/else 'L'
end -- for (segCt)
-- deal with "last seg is loop"
if ( inside == true ) then
loopEnds[ #loopEnds + 1 ] = MaxUnlockedSeg
end
end
function GetRegionStartAndEnd( idx )
s = idx
e = idx
if not IsUnlockedSeg( idx ) then return idx,idx end
local typeIdx = structure.GetSecondaryStructure( idx )
if idx > MinUnlockedSeg then
s = idx - 1
while s >= MinUnlockedSeg do
if structure.GetSecondaryStructure( s ) ~= typeIdx or not IsUnlockedSeg( s ) then break end
s = s - 1
end
s = s + 1
end
if idx < MaxUnlockedSeg then
e = idx + 1
while e <= MaxUnlockedSeg do
if structure.GetSecondaryStructure( e ) ~= typeIdx or not IsUnlockedSeg( e ) then break end
e = e + 1
end
e = e - 1
end
return s, e
end
-- uses GetRegionStartAndEnd, but tries to tune "loop" regions a bit
function GetRegionForOperation( idx )
-- try to find an acceptable region (really bizarre choices of SS can give trouble)
if not IsUnlockedSeg( idx ) then
return idx, idx
end
sIdx, eIdx = GetRegionStartAndEnd( idx )
-- Check: if our entire region is only 1 or 2 segs long, then see about expanding it
local regionType = structure.GetSecondaryStructure( idx )
if eIdx - sIdx <= 1 then
-- small range. Try to expand it...
local sIdx2, eIdx2
if sIdx > 2 then
if structure.GetSecondaryStructure(sIdx - 1) == regionType then
sIdx2, eIdx2 = GetRegionStartAndEnd( sIdx - 1)
sIdx = sIdx2
end
end
if eIdx < segCt - 1 then
if structure.GetSecondaryStructure( eIdx + 1 ) == regionType then
sIdx2, eIdx2 = GetRegionStartAndEnd( eIdx + 1 )
eIdx = eIdx2
end
end -- END try to fix small range
if eIdx - sIdx <= 1 then return idx,idx end -- failed
end
-- Check: if loop and more than 30-long, then only take part of it
if regionType == 'L' and eIdx - sIdx > 30 then
-- trim it; it is too long
if idx - sIdx < 15 then
eIdx = idx + 15 -- trim the other, longer side
elseif eIdx - idx < 15 then
sIdx = idx - 15 -- trim the other, longer side
else
-- more than 15 segs slop on both sides!
-- try to be random and "medium-long": 3d5 on each side should do
sIdx = idx - randomDice(3, 1, 5)
eIdx = idx + randomDice(3, 1, 5)
end
end
return sIdx, eIdx
end
function ResetSecondaryStructures( min, max )
selection.DeselectAll( )
for i=min, max do
if SecStructList[ i ] == "l" then
selection.Select( i )
end
end
structure.SetSecondaryStructureSelected( "l" )
selection.DeselectAll( )
for i=min, max do
if SecStructList[ i ] == "e" then
selection.Select( i )
end
end
structure.SetSecondaryStructureSelected( "e" )
selection.DeselectAll( )
for i=min, max do
if SecStructList[ i ] == "h" then
selection.Select( i )
end
end
structure.SetSecondaryStructureSelected( "h" )
selection.DeselectAll( )
end
function StoreSecondaryStructure( )
for i=1, segCt do
SecStructList[ i ] = structure.GetSecondaryStructure( i )
end
end
function SetSS( min, max, type )
selection.DeselectAll( )
selection.SelectRange( min, max )
structure.SetSecondaryStructureSelected( type )
selection.DeselectAll( )
end
function SetAllLoop( min, max )
SetSS( min, max, "l" )
end
----------------------------------------------------------------
-- CLASH IMPORTANCE
----------------------------------------------------------------
function SetCI( ci )
behavior.SetClashImportance( SCALE_MAXCI * ci )
end
function ExactSetCI( ci )
behavior.SetClashImportance( ci )
end
----------------------------------------------------------------------
-- CUTTING FUNCTIONS
----------------------------------------------------------------------
function DeleteCutAtIndex( idx )
if idx == nil or idx < 1 or idx > segCt then return true end
local sc = getQuickScore( )
structure.DeleteCut( idx )
return getQuickScore( ) ~= sc -- if no score change at all, then cut deletion didn't happen!
end
function AddRandomCuts( cutList, maxCuts )
for i=1, maxCuts do
local seg = randomUnlockedSeg( )
local gotMatch = false
for j=1, #cutList do
if cutList[ j ] == seg then gotMatch = true end
end
if not gotMatch then
structure.InsertCut( seg )
cutList[ #cutList + 1 ] = seg
end
end
end
function CleanCutsInList( cutList )
changeSucceeded = true
for i=1, #cutList do
changeSucceeded = DeleteCutAtIndex( cutList[ i ] ) and changeSucceeded
end
return changeSucceeded
end
function ResetCuts( )
if not PRESERVE_USER_CUTS then
for i=1, segCt do DeleteCutAtIndex( i ) end -- any cuts that appeared better go now, too
end
end
----------------------------------------------------------------------
-- BARE-BONE REBUILD FUNCTION
----------------------------------------------------------------------
function RebuildSelected( iters )
band.DisableAll( )
structure.RebuildSelected( iters )
band.EnableAll( )
end
----------------------------------------------------------------
-- MULTI-TRY ACTION CONTROLLER
----------------------------------------------------------------
function MultitryActOnRegion( NumberOfStarts, NumberOfActionsPerTry, startIdx, endIdx, ActionFunction )
print( "MultitryActOnRegion for "..NumberOfStarts.." starts with "..NumberOfActionsPerTry.." steps" )
BestScore=getScore( )
LoadBest( )
save.Quicksave( QS_NeighborTemp1 )
save.Quicksave( QS_NeighborTemp2 )
save.Quicksave( QS_NeighborTemp3 )
for k=1, NumberOfStarts do
save.Quickload( QS_NeighborTemp1 ) -- always start over from initial state
save.Quicksave( QS_NeighborTemp2 )
print( "Starting try " .. k.. ": Score="..getScore( ) )
local initscore = getScore( )
for j=1, NumberOfActionsPerTry do -- use "best so far"
save.Quickload( QS_NeighborTemp2 )
local currScore = getScore( )
ActionFunction( startIdx, endIdx )
local endScore = getScore( )
if endScore > currScore then
print( ">>>> At ".. j ..": score="..endScore )
save.Quicksave( QS_NeighborTemp2 )
currScore = endScore
end
if endScore > BestScore then
save.Quicksave( QS_NeighborTemp3 )
BestScore = endScore
end
SaveBest( )
end -- FOR j<actions
save.Quickload( QS_NeighborTemp2 )
local endScore = getScore( )
print( "Finishing try " .. k.. " Score: "..endScore )
end -- FOR k<starts
LoadBest( )
end
------------------------------------------------------------------------------
--
---- WIGGLER FUNCTIONS
--
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- GENERAL-PURPOSE SHAKERS AND WIGGLERS
------------------------------------------------------------------------------
-- no recentbest here because sometimes we'd call while decreasing score
function ShakeOrMutate( iters, idxList )
local startTime = os.time( )
if iters == nil then iters = 1 end
if idxList == nil then
if MUTATE_NOT_SHAKE then
structure.MutateSidechainsAll( iters )
else
structure.ShakeSidechainsAll( iters )
end
else
selection.DeselectAll( )
for i=1, #idxList do selection.Select( i ) end
if MUTATE_NOT_SHAKE then
structure.MutateSidechainsSelected( iters )
else
structure.ShakeSidechainsSelected( iters )
end
selection.DeselectAll( )
end
TotalShakeTime = TotalShakeTime + ( os.time( ) - startTime )
end
-- no recentbest here because sometimes we'd call while decreasing score
function WiggleRange( iters, startSeg, endSeg, addFreeze, doGlobal )
local firstSeg = math.max( MinUnlockedSeg, startSeg )
local lastSeg = math.min( endSeg, MaxUnlockedSeg )
if lastSeg < firstSeg then return end -- nothing to do
-- set up for the local wiggle
selection.DeselectAll( )
if addFreeze then
ChangedFrozenness = true
if firstSeg > MinUnlockedSeg then freeze.Freeze( firstSeg - 1, true, true ) end
if lastSeg < MaxUnlockedSeg then freeze.Freeze( lastSeg + 1, true, true ) end
end
-- actually perform the local wiggle
selection.SelectRange( firstSeg, lastSeg )
if doGlobal then
structure.WiggleSelected( iters, true, true )
else
structure.LocalWiggleSelected( iters, true, true )
end
-- clean up what we did
ResetFrozenness( )
selection.DeselectAll( )
end
-- no recentbest here because sometimes we'd call while decreasing score
function IterativeWiggleAll( iters, minGain, doBackbone, doSidechain )
local lastScore = getQuickScore( )
local gain = minGain
recentbest.Save( )
while gain >= minGain do
structure.WiggleAll( iters, doBackbone, doSidechain ) -- do's ok for nil
recentbest.Restore( )
gain = getQuickScore( ) - lastScore
lastScore = getQuickScore( )
end
end
---------------------------------------------
-- SIMPLE STABILIZER WIGGLES
---------------------------------------------
function IterativeWiggleList( idxList, iters, minGain, doBackbone, doSidechain )
selection.DeselectAll( )
for i=1, #idxList do selection.Select( i ) end
local lastScore = getQuickScore( )
local gain = minGain
while gain >= minGain do
structure.WiggleSelected( iters, doBackbone, doSidechain ) -- do's ok for nil
gain = getQuickScore( ) - lastScore
lastScore = getQuickScore( )
end
selection.DeselectAll( )
end
function IterativeWiggleShakeWiggle( idxList, iters )
recentbest.Save( )
sc = getQuickScore( )
IterativeWiggleList( idxList, iters, QUICKWIGGLE_GAIN )
recentbest.Restore( )
ShakeOrMutate( SHAKESIDECHAIN_ITERS, idxList )
recentbest.Restore( )
if math.abs( getQuickScore( ) - sc) > 1 then -- otherwise, why bother?
IterativeWiggleAll(iters, QUICKWIGGLE_GAIN, true, true )
end
recentbest.Restore( )
end
function qStabWiggle( iters, doExtraWork )
if doExtraWork == nil then doExtraWork = false end
SetCI( 1.0 )
recentbest.Save( )
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
recentbest.Restore( )
if doExtraWork then
SetCI( 0.4 )
IterativeWiggleAll( iters, QUICKWIGGLE_GAIN, true, true )
recentbest.Restore( )
SetCI( 1.0)
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
recentbest.Restore( )
end
SetCI( 1.0)
IterativeWiggleAll( iters, QUICKWIGGLE_GAIN, true, true )
recentbest.Restore( )
end
-- the only user for now is the idealizer
function WiggleZlbNicely( iters, strength, skipLower, skipUpper )
if skipLower == nil then skipLower = MaxUnlockedSeg+1 end
if skipUpper == nil then skipUpper = MinUnlockedSeg-1 end
recentbest.Save( )
ZLB(strength, skipLower, skipUpper)
IterativeWiggleAll( iters, QUICKWIGGLE_GAIN, true, true )
recentbest.Restore( )
DelBands( )
recentbest.Save( )
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
recentbest.Restore( )
end
function SimpleRegionWiggle( iters, idxLo, idxHi, pickGlobalWiggle, wiggleExpansion )
if wiggleExpansion == nil then
wiggleExpansion = 1 + random( math.floor( segCt / 6 ))
end
local startIdx = math.max( MinUnlockedSeg, idxLo - wiggleExpansion )
local endIdx = math.min( MaxUnlockedSeg, idxHi + wiggleExpansion )
WiggleRange( iters, startIdx, endIdx, false, pickGlobalWiggle )
end
---------------------------------------------------------
--BAND-CHANGING WRAPUP WIGGLERS
---------------------------------------------------------
function BandedWiggleUntilEnoughChange( iters, scoreThreshold, pullingCI )
if pullingCI == nil then pullingCI = COREPULLING_CI end
SetCI( pullingCI )
local ss=getQuickScore( )
for str=0.30, 1.50, 0.11 do --search enough band strength to move
SetMyBandStrengths( str )
if random( ) < PROB_LOCALWIGGLE then
structure.LocalWiggleAll( iters )
else
structure.WiggleAll( iters )
end
if math.abs( ss-getQuickScore( ) ) > scoreThreshold then
-- we've changed the structure "enough", send it off
break
end
end
SetCI( 1.0 )
return math.abs( ss - getQuickScore( ) )
end
function SimpleBandedWiggleWrapper( iters, minChangeWanted, pullingCI )
print( "SimpleBandedWiggleWrapper" )
local scoreThreshold = math.min( minChangeWanted, getQuickScore( ) / 500.0)
recentbest.Save( )
local oldRbScore = getRBScore( )
BandedWiggleUntilEnoughChange( iters, scoreThreshold, pullingCI )
local newRbScore = getRBScore( )
if newRbScore > oldRbScore then recentbest.Restore( ) end
DelBands( )
ResetFrozenness( )
recentbest.Save( )
qStabWiggle( iters, random( ) < PROB_QSTAB_EXTRA )
recentbest.Restore( )
end
function BandedWiggleUntilTargetMet( iters, scoreDeltaTarget, maxTries, pullingCI )
print( "BandedWiggleUntilTargetMet" )
save.Quicksave( QS_ShortUseTemp1 )
save.Quicksave( QS_ShortUseTemp2 )
if pullingCI == nil then pullingCI = COREPULLING_CI end
SetCI( pullingCI )
local s1=getQuickScore( )
local str=0.50
local tries = 0
local s2best = 99999
while tries < maxTries do
save.Quickload( QS_ShortUseTemp1)
SetMyBandStrengths( str)
structure.WiggleAll( iters )
local s2 = math.abs( getQuickScore( ) - s1 )
if s2 > scoreDeltaTarget * 1.2 then
str = str * ( 0.85 + random( 0.10 ) )
elseif s2 < scoreDeltaTarget * 0.8 then
str = str + ( 1.0 - str ) * ( 0.05 + random( 0.1 ) )
else
return math.abs( getQuickScore( ) - s1 ) -- we're good enough
end
if math.abs( s2 - scoreDeltaTarget ) < s2best then
save.Quicksave( QS_ShortUseTemp2 )
s2best = math.abs( s2 - scoreDeltaTarget )
end
tries = tries + 1
end
save.Quickload( QS_ShortUseTemp2 ) -- the best we could come up with...
return math.abs( getQuickScore( ) - s1 ) -- returns the actual delta in case caller cares
end
function RegionWiggleBeforeAndAfter( iters, startIdx, endIdx )
print( "RegionWiggleBeforeAndAfter" )
recentbest.Save( )
local oldRbScore = getRBScore( )
SetCI( COREPULLING_CI )
SimpleRegionWiggle( iters, startIdx, endIdx, false, random( 4 ) )
local newRbScore = getRBScore( )
if newRbScore > oldRbScore then recentbest.Restore( ) end
DelBands( ) -- if we put in bands, they should go now
ResetFrozenness( ) -- if we froze anything, we should clean that up now
SetCI( 1.0 )
if random( ) < 0.50 then
qStabWiggle( iters, random( ) < PROB_QSTAB_EXTRA )
else
recentbest.Save( )
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
recentbest.Restore( )
SimpleRegionWiggle( iters, startIdx, endIdx, true, random( 4, 8 ) )
recentbest.Restore( )
IterativeWiggleAll( iters, QUICKWIGGLE_GAIN, true, true )
end
recentbest.Restore( )
end
function GlobalBeforeAndQStabAfter( iters )
print( "GlobalBeforeAndQStabAfter" )
recentbest.Save( )
local oldRbScore = getRBScore( )
SetCI( COREPULLING_CI )
if random( ) < PROB_LOCALWIGGLE then
structure.LocalWiggleAll( QUICKWIGGLE_ITERS )
else
structure.WiggleAll( QUICKWIGGLE_ITERS )
end
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
local newRbScore = getRBScore( )
if newRbScore > oldRbScore then recentbest.Restore( ) end
SetCI( 1.0 )
DelBands( ) -- if we put in bands, they should go now
ResetFrozenness( ) -- if we froze anything, we should clean that up now
recentbest.Save( )
qStabWiggle( iters, random( ) < PROB_QSTAB_EXTRA )
recentbest.Restore( )
end
function VerySimpleWiggleBeforeAndAfter( iters )
recentbest.Save( )
local oldRbScore = getRBScore( )
ShakeOrMutate( 1 )
if random( ) < PROB_LOCALWIGGLE then
structure.LocalWiggleAll( 1 )
else
structure.WiggleAll( 1 )
end
local newRbScore = getRBScore( )
if newRbScore > oldRbScore then recentbest.Restore( ) end
DelBands( )
ResetFrozenness( )
recentbest.Save( )
ShakeOrMutate( 1 )
IterativeWiggleAll( iters, QUICKWIGGLE_GAIN, true, true )
recentbest.Restore( )
end
function VerySimpleNonBandedWiggler( iters )
recentbest.Save( )
ExactSetCI( 0.05 )
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
SetCI( 1.0 )
IterativeWiggleAll( iters, QUICKWIGGLE_GAIN, true, true )
recentbest.Restore( )
end
function PickBeforeAndAfterWiggler( doVariableStrength )
local iters = QUICKWIGGLE_ITERS
if USE_LOWCI_WIGGLES then
VerySimpleWiggleBeforeAndAfter( iters )
elseif doVariableStrength then
SimpleBandedWiggleWrapper( iters, BANDEDWIGGLE_TARGET, COREPULLING_CI )
else
GlobalBeforeAndQStabAfter( iters )
end
end
function nudge( )
SetCI( 0.3 )
structure.WiggleSelected( 1 )
SetCI( 1 )
structure.WiggleSelected( 8 )
end
function WiggleAndShakeForRebuilder( initScore, maxFall )
print( "WiggleAndShakeForRebuilder" )
-- initScore is the score before our rebuild happened
recentbest.Save( )
scoreA = getQuickScore( )
if initScore - scoreA > maxFall then
ShakeOrMutate( 2*SHAKESIDECHAIN_ITERS )
elseif initScore - scoreA > maxFall/2 then
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
end
scoreA = getQuickScore( )
if random( ) < PROB_LOCALWIGGLE then
structure.LocalWiggleAll( 15 )
else
structure.WiggleAll( 15 )
end
recentbest.Restore( )
scoreB = getQuickScore( )
if scoreB - scoreA < 10 and initScore - scoreB > 30 then
-- The wiggle got stuck and didn't achieve anything. try a nudge to see if that helps
nudge( )
recentbest.Restore( )
scoreB = getQuickScore( )
end
if initScore - scoreB <= 100.0 then
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
structure.WiggleSelected( 10 )
recentbest.Restore( )
nudge( )
recentbest.Restore( )
end -- else not worth the effort
end
--------------------------------------------------
-- SLOW CLEANUP WIGGLERS
--------------------------------------------------
function BlueFuse( )
recentbest.Save( )
SetCI( .05 )
structure.ShakeSidechainsAll( SHAKESIDECHAIN_ITERS )
SetCI( 1 )
structure.WiggleAll( 5 )
recentbest.Restore( )
SetCI( .07 )
structure.ShakeSidechainsAll( SHAKESIDECHAIN_ITERS )
SetCI( 1 )
structure.WiggleAll( 5 )
recentbest.Restore( )
SetCI( .3 )
structure.WiggleAll( 8 )
SetCI( 1 )
structure.WiggleAll( 15 )
recentbest.Restore( )
end
function IterativeWiggleLocalByChunk(iters, startSeg, endSeg, minGain, chunkSize, addFreeze)
recentbest.Save( )
currOffset = 0
local lastScore = getQuickScore( )
gain = minGain
while gain >= minGain do
if chunkSize == 1 then currOffset = 0
else currOffset = random( chunkSize - 1 )
end
ctBlocks = math.floor( (endSeg - startSeg + 1) / chunkSize )
for idx = 0,ctBlocks do
local score = getQuickScore( )
startIdx = startSeg + currOffset + chunkSize*idx
WiggleRange(iters, startIdx, math.min( startIdx + chunkSize, endSeg ), addFreeze, false )
recentbest.Restore( )
if addFreeze then ResetFrozenness( ) end
end
local currScore = getQuickScore( )
gain = currScore - lastScore
lastScore = currScore
end
recentbest.Restore( )
if addFreeze then ResetFrozenness( ) end
end
function BasicCutAndWiggleFuse( baseIters, shift, offset )
if shift == nil then shift = 3 end
if offset == nil then offset = MinUnlockedSeg else offset = MinUnlockedSeg + (offset % shift) end
for i = offset, MaxUnlockedSeg-1, shift do
structure.InsertCut( i )
end
WiggleWiggleSimpleFuse( 0.30, 1.0, baseIters, 2*baseIters )
ResetCuts( )
WiggleWiggleSimpleFuse( 0.30, 1.0, baseIters, 4*baseIters )
SetCI( 1.0 )
end
function IterativeCutAndWiggle( baseIters, minGain, shift )
local score = getQuickScore( )
local gain = minGain
save.Quicksave( QS_ShortUseTemp1 )
recentbest.Save( )
repeat
save.Quickload( QS_ShortUseTemp1 )
BasicCutAndWiggleFuse( baseIters, shift, random( shift ) )
local newscore = getQuickScore( )
gain = 0.0
if newscore >= score then
save.Quicksave( QS_ShortUseTemp1 )
gain = newscore - score
score = newscore
end
until gain < minGain
save.Quickload( QS_ShortUseTemp1 )
recentbest.Restore( )
end
--------------------------------------------------
-- BASIC FUSE WIGGLERS
--------------------------------------------------
function WiggleShakeSimpleFuse( ci, iters )
SetCI( ci )
structure.WiggleAll( iters ) -- instead of IterativeWiggleAll...
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
end
function WiggleWiggleSimpleFuse( ci1, ci2, shortiter, longiter )
recentbest.Save( )
local oldRbScore = getRBScore( )
SetCI( ci1 )
structure.WiggleAll( shortiter )
local newRbScore = getRBScore( )
if newRbScore > oldRbScore then recentbest.Restore( ) end
SetCI( ci2 )
recentbest.Save( )
structure.WiggleAll( longiter ) -- "long" wiggle
recentbest.Restore( )
end
function ShakeWiggleFuse( ci1, ci2, iters ) -- aka Fuze1
SetCI( ci1 )
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
SetCI( ci2 )
IterativeWiggleAll( iters, QUICKWIGGLE_GAIN, true, true )
end
function WiggleWiggleWiggleFuse( ci1, ci2, iters ) -- aka Fuze2
SetCI( ci1 )
IterativeWiggleAll( iters, QUICKWIGGLE_GAIN, true, true )
SetCI( 1.00 )
IterativeWiggleAll( iters, QUICKWIGGLE_GAIN, true, true )
SetCI( ci2 )
IterativeWiggleAll( iters, QUICKWIGGLE_GAIN, true, true )
end
function WiggleShakeWiggleFuse( iters ) -- aka FuzeEnd
SetCI( 1.00 )
IterativeWiggleAll( iters, QUICKWIGGLE_GAIN, true, true )
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
IterativeWiggleAll( iters, QUICKWIGGLE_GAIN, true, true )
end
----------------------------------------------------------------
-- THE CLEANUP WIGGLER
----------------------------------------------------------------
function DoCleanupWiggle( doFuseWiggle, iters, minGainForIteration )
SetCI( 1.0 )
recentbest.Save( )
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
recentbest.Restore( )
oldScore = getQuickScore( )
oldTime = os.time( )
algName = "IterativeWiggleAll"
IterativeWiggleAll( iters, minGainForIteration, true, true)
recentbest.Restore( )
deltaScore = getQuickScore( ) - oldScore
deltaTime = os.time( ) - oldTime
AddActionToStats( deltaScore, deltaTime )
oldScore = getQuickScore( )
oldTime = os.time( )
if doFuseWiggle and deltaScore <= SLOWWIGGLE_THRESHOLD then
local r = random( )
if r < PROB_SLOWWIGGLE_LOCAL then
print( "IterativeWiggleLocalByChunk" )
algName = "IterativeWiggleLocalByChunk"
IterativeWiggleLocalByChunk( iters, 1, segCt, minGainForIteration, random( 3, 6 ), false )
elseif r < PROB_SLOWWIGGLE_LOCAL + PROB_SLOWWIGGLE_BLUEFUSE then
print( "BlueFuse" )
algName = "BlueFuse"
BlueFuse( )
else -- PROB_SLOWWIGGLE_CUTFUSE
print( "IterativeCutAndWiggle")
algName = "IterativeCutAndWiggle"
IterativeCutAndWiggle( iters, minGainForIteration, random( 3,11 ) )
end
end
local deltaScore = getQuickScore( ) - oldScore
local deltaTime = os.time( ) - oldTime
AddActionToStats( deltaScore, deltaTime )
recentbest.Restore( )
if PROB_SLOWWIGGLE_CUTFUSE > 0.0 and USE_SLOWWIGGLE then
for i=1, segCt do -- deliberate non-use of ResetCuts
DeleteCutAtIndex( i )
end
end
end
----------------------------------------------------------------------
--
---- BANDER FUNCTIONS
--
----------------------------------------------------------------------
function DelBands( )
if not PRESERVE_USER_BANDS then
band.DeleteAll( )
else
local ct = band.GetCount( )
for i=ct, InitialBandCount+1, -1 do band.Delete( i ) end
end
end
function SetMyBandStrengths( strength )
for i=InitialBandCount+1, band.GetCount( ) do
band.SetStrength( i, strength )
end
end
function BandBetweenSegsWithParameters( seg1, seg2, strength, goalLength, atom1, atom2 )
if not ( IsUnlockedSeg( seg1 ) or IsUnlockedSeg( seg2 ) ) then return false end
if atom1 == nil then atom1 = CENTER_CARBON end
if atom2 == nil then atom2 = CENTER_CARBON end
local bIdx = band.AddBetweenSegments( seg1, seg2, atom1, atom2 )
if bIdx ~= band.GetCount( ) then
DebugPrint( "failed to add band from "..seg1.." to "..seg2)
return false
end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands
if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength > 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end
function BandInSpaceWithParameters( seg, segFini, segInit, rho, theta, phi, strength, goalLength )
if not ( IsUnlockedSeg( seg) ) then return false end
local bIdx = band.Add( seg, segFini, segInit, rho, theta, phi )
if bIdx ~= band.GetCount( ) then return false end
if bIdx <= InitialBandCount then return true end -- don't change user-supplied bands
if goalLength ~= nil then
band.SetGoalLength( bIdx, goalLength )
end
if strength ~= nil and strength ~= 0.0 then
band.SetStrength( bIdx, strength )
end
return true
end
function ZLB( strength, skipLower, skipUpper )
if skipLower == nil then skipLower = MaxUnlockedSeg + 1 end
if skipUpper == nil then skipUpper = MinUnlockedSeg - 1 end
for i=MinUnlockedSeg+1, MaxUnlockedSeg-1 do
if i < skipLower or i > skipUpper then
BandInSpaceWithParameters( i, i+1, i-1, 0.01, 0.0, 0.0, strength, 0.01)
end
end
if skipLower > 1 then
BandInSpaceWithParameters( 1, 2, 3, 0.01, 0.0, 0.0, strength, 0.01)
end
if skipUpper < segCt then
BandInSpaceWithParameters( segCt, segCt-1, segCt-2, 0.01, 0.0, 0.0, strength, 0.01)
end
end
----------------------------------------------------------------------
-- SMALL-SCALE BANDERS
-- try making a cysteine joiner
----------------------------------------------------------------------
function PutBandInSpace( idx, maxRho, strength )
local idx2, idx3
if idx < segCt then
idx2 = idx + 1
else
idx2 = idx - 2
end
if idx > 1 then
idx3 = idx - 1
else
idx3 = idx + 2
end
-- random point in sphere of radius maxRho
local theta, phi = randomThetaPhi( )
local rho = ( maxRho * random( )^(1/3) ) + 0.001
return BandInSpaceWithParameters( idx, idx2, idx3, rho, theta, phi, strength )
end
function PutBandToRandomSeg( idx, strength, atom )
needNew = true
local failedTries = 0
while ( needNew and failedTries < 20 ) do
idx2 = randomSeg( ) -- ok if this one isn't movable (we assume idx is movable)
if idx2 > idx + 1 or idx2 < idx - 1 then
needNew = false
break
else
failedTries = failedTries + 1
end
end
if not needNew then
return BandBetweenSegsWithParameters( idx, idx2, strength, nil, atom, nil )
end
return false
end
function PutGlycineHingeBands( glycineIdx, strength )
print( "PutGlycineHingeBands at ".. glycineIdx )
if glycineIdx == 1 or glycineIdx == segCt then return false end
changeSucceeded = false
selection.DeselectAll ( )
-- construct the rods for the hinge (freeze before and after the glycine)
local sR, eR = GetRegionStartAndEnd( glycineIdx + 1 )
if eR < segCt and eR - glycineIdx < 3 then eR = eR + 1 end
selection.SelectRange ( glycineIdx + 1 , eR )
local sL, eL = GetRegionStartAndEnd( glycineIdx - 1 )
if eL > 1 and glycineIdx - eL < 3 then eL = eL - 1 end
selection.SelectRange ( sL, glycineIdx - 1 )
ChangedFrozenness = true
freeze.FreezeSelected ( true, true )
selection.DeselectAll ( )
-- put bands connecting the two rods
local maxLen = math.min( eR - sR + 1, eL - sL + 1 )
if maxLen > 4 or (glycineIdx >= 4 and glycineIdx <= segCt - 4) then
maxLen = 4 -- try very hard to have 4 instead of only 2 or 3, even if it means bands beyond end of rods
elseif maxLen < 1 then
return changeFailed -- should only happen if glycine at an end
end
for i=1, maxLen do
if BandBetweenSegsWithParameters(
math.max(1, glycineIdx - i),
math.min(segCt, glycineIdx + i),
strength )
then
changeSucceeded = true
end
end
return changeSucceeded
end
----------------------------------------------------------------------
-- MID-SCALE BANDERS
----------------------------------------------------------------------
function PutGeneralHingeBands( startHinge, endHinge, strength )
print( "PutGeneralHingeBands at "..startHinge.." to "..endHinge )
if startHinge > endHinge then startHinge, endHinge = endHinge, startHinge end
if startHinge == 1 or endHinge == segCt then return false end
changeSucceeded = false
selection.DeselectAll( )
-- construct the rods for the hinge (freeze before and after the region)
local sR, eR = GetRegionStartAndEnd( endHinge + 1 )
if eR < segCt and eR - endHinge < 3 then eR = eR + 1 end
selection.SelectRange( endHinge + 1 , eR )
local sL, eL = GetRegionStartAndEnd( startHinge - 1 )
if eL > 1 and startHinge - eL < 3 then eL = eL - 1 end
selection.SelectRange( sL, startHinge - 1 )
ChangedFrozenness = true
freeze.FreezeSelected( true, true )
selection.DeselectAll( )
-- put bands connecting the two rods
local maxLen = math.min( eR - sR + 1, eL - sL + 1 )
if maxLen > 4 then maxLen = 4 -- we will surely have it be at least 2 by choice of eR and eL
elseif maxLen < 1 then return changeFailed -- should only happen if startHinge or endHinge is at an end
end
for i=1, maxLen do
if BandBetweenSegsWithParameters(
math.max(1, startHinge - i),
math.min(segCt, endHinge + i),
strength )
then
changeSucceeded = true
end
end
return changeSucceeded
end
function PutPushPullBands( badSeg, bandsWanted, center, strength, wantPushNotPull )
print( "PutPushPullBands at "..badSeg.." centered at "..center )
changeSucceeded = false
local okSegList = {}
local SC = structure.GetDistance( badSeg, center )
local SR
for i=1, segCt do
local RC=structure.GetDistance( i, center )
SR=structure.GetDistance( i, badSeg )
if SR<15
and RC<SC-3
and math.abs( i - badSeg ) > 5
and math.abs( i - center ) > 3
then
okSegList[#okSegList + 1] = i
end
end
randomizeIndexList( okSegList )
local bandsAdded = 0
for i=1, #okSegList do
SR=structure.GetDistance( i, badSeg )
if wantPushNotPull then
SR=SR + 4
else
SR=SR - 4
end
if SR>20 then SR=20 end
if SR<3 then SR=3 end
if BandBetweenSegsWithParameters( badSeg, okSegList[i], strength, SR ) then
changeSucceeded = true
bandsAdded = bandsAdded + 1
end
if bandsAdded >= bandsWanted then break end
end -- FOR(i)
return changeSucceeded
end
function PutVoidCrusherBands( idx, strength ) --make band if found void in area of segment "idx"
print( "PutVoidCrusherBands at "..idx )
changeSucceeded = false
local t={} -- store possible segments
for b=1,segCt do --test all segments
if math.abs(idx-b) >= 15 then -- else idx,b too near each other count-wise
local ab = structure.GetDistance(idx, b)
if ab > 10.0 then --no void if less
local void=true
for c=1,segCt do --searching for any segment between them
if c ~= idx and c ~= b then -- don't want c = idx or b
local ac = structure.GetDistance(idx,c)
local bc = structure.GetDistance(b,c)
if ac<ab and bc<ab and ac>4 and bc>4 then
if ac+bc<ab+1.5
then void=false break --no void there for sure
end
end
end
end -- END for c
if void==true then
t[#t+1]={idx,b}
end
end -- END if idx and b are spatially at least 10 apart
end -- END if idx and b are count-wise at least 15 apart
end -- LOOP over candidate b's
if #t>0 then
for i=1,#t do
local s=t[i][1]
local e=t[i][2]
local d=structure.GetDistance( s, e )
d = d - 3.0 -- try to compress this much...
if d<3 then d=3 end
if d>20 then d=20 end
if BandBetweenSegsWithParameters( s, e, strength, d ) then changeSucceeded = true end
end
end
return changeSucceeded
end
function PutHelixUniformDistanceBands( startSeg, endSeg, shift, strength )
print( "PutHelixUniformDistanceBands at "..startSeg..", "..endSeg )
changeSucceeded = false
sumDist = 0
for i=startSeg, endSeg-shift do
sumDist = sumDist + structure.GetDistance( i, i+shift )
end
length = sumDist/( endSeg - shift - startSeg + 1 )
for i=startSeg,endSeg-shift do
if BandBetweenSegsWithParameters( i, i+shift, strength, length ) then changeSucceeded = true end
end
return changeSucceeded
end
function PutHelixFixerBands( startSeg, endSeg, strength, do4, do5 )
print( "PutHelixFixerBands at "..startSeg..", "..endSeg )
changeSucceeded = false
if do4 then
for i=startSeg,endSeg-4 do
if BandBetweenSegsWithParameters( i, i+4, strength, HELIX_DISTANCE_SKIP4 ) then changeSucceeded = true end
end
end
if do5 then
for i=startSeg,endSeg-5 do
if BandBetweenSegsWithParameters( i, i+5, strength, HELIX_DISTANCE_SKIP5 ) then changeSucceeded = true end
end
end
return changeSucceeded
end
function PutHelixCompressorBands( startSeg, endSeg, shift, strength, stretch )
print( "PutHelixCompressorBands at "..startSeg..", "..endSeg )
changeSucceeded = false
for i=startSeg,endSeg-shift do
dist = structure.GetDistance( i, i+shift )
if BandBetweenSegsWithParameters( i, i+shift, strength, dist*stretch ) then changeSucceeded = true end
end
return changeSucceeded
end
function PutRotatorBands( startSeg, endSeg, length, strength, wantClockwise )
print( "PutRotatorBands at "..startSeg..", "..endSeg )
changeSucceeded = false
for i = math.max( startSeg , 2 ), math.min( endSeg , segCt - 1 ) do
if wantClockwise then
if BandInSpaceWithParameters( i, i + 1, i - 1, length, math.pi/2, math.rad ( 315 ), strength ) then
changeSucceeded = true
end
else
if BandInSpaceWithParameters( i, i - 1, i + 1, length, math.pi/2, math.rad ( 315 ), strength ) then
changeSucceeded = true
end
end
end
return changeSucceeded
end
function PutPusherBands( startSeg, endSeg, length, strength, doForward )
print( "PutPusherBands at "..startSeg..", "..endSeg )
changeSucceeded = false
if startSeg > 1 then
init = startSeg - 1
else
init = startSeg
end
if endSeg < segCt then
fini = endSeg + 1
else
fini = endSeg
end
if not doForward then init, fini = fini, init end
for i = init + 1, fini - 1 do
if BandInSpaceWithParameters( i, fini, init, length, math.pi/2, 0.0, strength ) then
changeSucceeded = true
end
end
return changeSucceeded
end
function PutMoverBands( startSeg, endSeg, length, strength )
print( "PutMoverBands at "..startSeg..", "..endSeg )
changeSucceeded = false
if startSeg > 1 then
init = startSeg - 1
else
init = startSeg
end
if endSeg < segCt then
fini = endSeg + 1
else
fini = endSeg
end
theta, phi = randomThetaPhi( )
for i = init + 1, fini - 1 do
if BandInSpaceWithParameters( i, fini, init, length, theta, phi, strength ) then
changeSucceeded = true
end
end
return changeSucceeded
end
-- TODO consider putting more bands in "between" segments, with lengths that increase
-- as they head towards the midpoint
function PutFlexerBands( startSeg, endSeg, length, strength )
print( "PutFlexerBands at "..startSeg..", "..endSeg )
if endSeg - startSeg < 3 then return false end
changeSucceeded = false
if startSeg > 1 then
init = startSeg - 1
first = startSeg
else
init = startSeg
first = startSeg + 1
end
if endSeg < segCt then
fini = endSeg + 1
last = endSeg
else
fini = endSeg
last = endSeg - 1
end
midpt = (startSeg + endSeg + 1) / 2
-- initial band points inwards
if BandInSpaceWithParameters( first, fini, init, 1.0, math.pi/2.0, 0.0, strength ) then
changeSucceeded = true
end
--final band points inwards, too
if BandInSpaceWithParameters( last, init, fini, 1.0, math.pi/2.0, 0.0, strength ) then
changeSucceeded = true
end
-- middle one is longer and points perpendicularly to init-fini line. Consider
-- replacing with ramping-up lengths over all "interior" segments.
theta, phi = randomThetaPhi( ) -- ignoring phi, because we just want to use 0
if BandInSpaceWithParameters( midpt, fini, init, length, theta, 0.0, strength ) then
changeSucceeded = true
end
return changeSucceeded
end
function PutSheetStraightenerBands( startSeg, endSeg, strength )
-- 9.5 is "ideal" distance between N and N+2 in sheets. Make a constant for it sometime
print( "PutSheetStraightenerBands at "..startSeg..", "..endSeg )
changeSucceeded = false
for i=startSeg, endSeg-2 do
changeSucceeded = BandBetweenSegsWithParameters( i, i+2, 9.5, strength ) or changeSucceeded
end
return changeSucceeded
end
-- bandToNearest=false means "band to farthest"
function PutManyBandsToSeg( idx, maxBandCount, strength, compression, bandToNearest )
changeSucceeded = false
local segsToBand = {}
local idxList = {}
local distList = {}
local totalDistance = 0.0
local dist = 0.0
-- we will construct segsToBand first, summing totalDistance as we go
for i = 3, segCt - 3 do
if i ~= idx and IsUnlockedSeg( i ) then
if bandToNearest then
if IsLocalMinimum( idx, i ) then
segsToBand[ #segsToBand + 1 ] = i
totalDistance = totalDistance + structure.GetDistance( idx, i )
end
else
if IsLocalMaximum( idx, i ) then
segsToBand[ #segsToBand + 1 ] = i
totalDistance = totalDistance + structure.GetDistance( idx, i )
end
end
end
end
-- now sort them so we can take only the "best" segments...
for i=1, #segsToBand do idxList[ i ] = i end
for i=1, #segsToBand do distList[ i ] = structure.GetDistance( idx, i ) end
sortAllByScore( idxList, distList )
-- now put the bands, preferring the "nearest" ones
for i=1, math.min( #segsToBand, maxBandCount ) do
local dist = structure.GetDistance( idx, segsToBand[ idxList[ i ] ] )
if BandBetweenSegsWithParameters( idx, segsToBand[ idxList[ i ] ], strength, compression * dist ) then
changeSucceeded = true
end
end
return changeSucceeded
end
----------------------------------------------------------------------
-- LARGE-SCALE BANDERS
----------------------------------------------------------------------
function PutSheetSewerBandsAllOver( maxDist, strength )
print( "SheetSewerBands: dist="..maxDist )
changeSucceeded = false
for seg1 = 2, segCt - 4 do
for seg2 = seg1 + 3, segCt do
if IsUnlockedSeg( seg1 ) or IsUnlockedSeg( seg2 ) then
if structure.GetDistance( seg1, seg2 ) < maxDist and
structure.GetSecondaryStructure( seg1 ) == 'E' and
structure.GetSecondaryStructure( seg2 ) == 'E'
then
local changeSuc = BandBetweenSegsWithParameters(seg1, seg2, strength )
changeSucceeded = changeSucceeded or changeSuc
end
end
end
end
return changeSucceeded
end
function PutCoreCompressBands(coreList, skipCt, startOffset, strength, compression, pullPhilics, doSidechains )
print( "PutCoreCompressBands for "..#coreList.." cores with compress="..TrimNum( compression ) )
if doSidechains == nil then doSidechains = false end
if pullPhilics == nil then pullPhilics = false end
local str = " connecting to "
if doSidechains then str=str .. "sides" else str = str .. "backbone" end
if pullPhilics then str=str .. ", pulling philics away" else str = str .. ", philics and phobics treated same" end
print( str )
local maxToBand = #coreList
local bandList = {}
local added = 0
-- first put bands between all of our central segs
-- notice: there's no protection from some of them being adjacent, but it's ok
-- if one or more of these bands fail to be created.
for i=1, maxToBand-1 do
for j=i+1, maxToBand do
if coreList[i] < coreList[j] - 1 or coreList[i] > coreList[j] + 1 then
local goalLength= compression * structure.GetDistance( coreList[ i ], coreList[ j ] )
if BandBetweenSegsWithParameters( coreList[ i ], coreList[ j ], strength, goalLength ) then
added = added + 1
end
end
end
end
-- now put bands between the "rest" of the protein and our chosen ones
for i=startOffset, segCt, skipCt do
local skipThisI = false
for j=1, maxToBand do
if coreList [ j ] == i then
skipThisI = true
break
end
end
if not skipThisI then
for j=1, maxToBand do
if i < coreList[ j ] - 1 or i > coreList[ j ] + 1 then
goalLength= compression * structure.GetDistance( i, coreList[ j ] )
if pullPhilics and not structure.IsHydrophobic( i ) then
goalLength= goalLength + 1.0
end
atom = nil
if BAND_TO_SIDECHAINS and doSidechains then
atom = PickAtomNumberForBand( i )
end
if BandBetweenSegsWithParameters( i, coreList[ j ], strength, goalLength, atom, nil ) then
added = added + 1
end
end
end
end
end
return added > 0
end
function PutBandsEveryXToEveryY( Xrange, Xinit, Yrange, Yinit, strength, compression )
print( "BandsXToY "..Xrange.." x "..Yrange.. " with compress="..TrimNum( compression ) )
local length = 0
local added = 0
for i=Xinit, segCt, Xrange do
for j=Yinit, segCt, Yrange do
if (i < j - 1 or i > j + 1) then
if compression == nil then length = nil
else length = compression * structure.GetDistance( i, j )
end
if BandBetweenSegsWithParameters( i, j, strength, length ) then
added = added + 1
end
end
end
end
return added > 0
end
function PutGentleCompBands( bandsWanted, minLength, maxLength, minSeparation, strength, compression )
print( "GentleCompBands: compression="..TrimNum( compression ) )
local bandsAdded = 0
local failCount = 0
repeat
local s = randomUnlockedSeg( )
local e = randomMovableFarSeg( s, minSeparation, minLength, maxLength )
if e == 0 then
failCount = failCount + 1
else
-- yes, this would try to add the same segment twice, but that will fail simply...
local dist = structure.GetDistance( s, e )
changeSucceeded = BandBetweenSegsWithParameters( s,e, strength, dist * compression )
if changeSucceeded then
bandsAdded = bandsAdded + 1
else
failCount = failCount + 1
end
end
until bandsAdded == bandsWanted or failCount >= 100
return true
end
----------------------------------------------------------------------
-- CONTACT-MAP BANDERS
----------------------------------------------------------------------
function PutAllContactMapBands( heatThreshold, madeStrength, madeLengthScale,
unmadeStrength, unmadeLengthScale,
doSidechains, ditherValues )
print( "AllCMBands" )
changeSucceeded = false
local baseStrength = strength
for i = 1, segCt-2 do
for j = i+2, segCt do
if contactmap.GetHeat(i, j) >= heatThreshold then
local goalLength = structure.GetDistance( i, j )
if contactmap.IsContact( i, j ) then
goalLength = madeLengthScale * goalLength
strength = madeStrength
else
goalLength = unmadeLengthScale * goalLength
strength = unmadeStrength
end
if ditherValues then
goalLength = random( 0.95, 1.05, true ) * goalLength
strength = random( 0.95, 1.05, true ) * strength
end
local atom1 = nil
local atom2 = nil
if BAND_TO_SIDECHAINS and doSidechains then
atom1 = BETA_CARBON
atom2 = BETA_CARBON
end
changeSucceeded = BandBetweenSegsWithParameters( i, j, strength, goalLength, atom1, atom2 )
or changeSucceeded
end
end
end
return changeSucceeded
end
function PutSomeContactMapBands( heatThreshold, strength, ctBands, doSidechains )
if ctBands > 1 then
print( "SomeCMBands with ct="..ctBands )
else
print( "SingleCMBand" )
end
changeSucceeded = false
local hotList = {}
for i = 1, segCt-2 do
for j = i+2, segCt do
local heat = contactmap.GetHeat(i, j)
if heat >= heatThreshold and not contactmap.IsContact( i , j ) then
hotList[ #hotList + 1] = { i, j, heat }
end
end
end
randomizeIndexList( hotList )
for i=1, math.min( ctBands, #hotList ) do
local atom1 = nil
local atom2 = nil
if BAND_TO_SIDECHAINS and doSidechains then
atom1 = PickAtomNumberForBand( hotList[i][1] )
atom2 = PickAtomNumberForBand( hotList[i][2] )
end
local ch = BandBetweenSegsWithParameters( hotList[i][1], hotList[i][2], strength, nil, atom1, atom2 )
changeSucceeded = ch or changeSucceeded
end
return changeSucceeded
end
function PutAllContactMapBandsToSeg( idx, heatThreshold, strength, doComp, doSidechain )
print( "AllCMBandsToSeg ".. idx )
changeSucceeded = false
local baseStrength = strength
if doComp then
-- COMP-style bands
for i=1, segCt do
if i < idx - 1 or i > idx + 1 then
local heat = contactmap.GetHeat( i, idx )
if heat >= heatThreshold then
local goalLength = structure.GetDistance( i, idx )
if contactmap.IsContact( i, idx ) then
strength = CONTACTMAP_CONTACTING_STRENGTH * baseStrength
goalLength = CONTACTMAP_CONTACTING_GOAL * goalLength
else
strength = CONTACTMAP_NONCONTACTING_STRENGTH * baseStrength
goalLength = CONTACTMAP_NONCONTACTING_GOAL * goalLength
end
local atom = nil
if BAND_TO_SIDECHAINS and doSidechain then
atom = PickAtomNumberForBand( i )
end
local changeSuc = BandBetweenSegsWithParameters(
idx, i, strength, goalLength,
nil, atom )
changeSucceeded = changeSuc or changeSucceeded
end
end
end
else
-- only bands to non-contacting segs
local hotList = {}
for i = 1, segCt do
if i < idx - 1 or i > idx + 1 then
local heat = contactmap.GetHeat( i, idx )
if heat >= heatThreshold and not contactmap.IsContact( i , idx ) then
hotList[ #hotList + 1] = i
end
end
end
for i=1, #hotList do
if hotList[ i ] ~= idx then -- not supposed to happen!
local atom = nil
if BAND_TO_SIDECHAINS and doSidechain then
atom = PickAtomNumberForBand( hotList[i] )
end
local changeSuc = BandBetweenSegsWithParameters(
idx, hotList[i], strength * contactmap.GetHeat( i, idx ),
nil, nil, atom )
changeSucceeded = changeSuc or changeSucceeded
end
end
end
return changeSucceeded
end
function PutContactMapBandsBetweenRegions(
heatThreshold,
startIdx1, endIdx1, startIdx2, endIdx2,
strength, doComp, doBetaCarbons, ditherValues )
print( "CMTwoRegions: ("..startIdx1..", "..endIdx1..") to ("..startIdx2..", "..endIdx2..")" )
changeSucceeded = false
local baseStrength = strength
local atom1 = CENTER_CARBON
local atom2 = CENTER_CARBON
if BAND_TO_SIDECHAINS and doBetaCarbons then
atom1 = BETA_CARBON
atom2 = BETA_CARBON
end
for i = startIdx1, endIdx1 do
for j = startIdx2, endIdx2 do
if contactmap.GetHeat( i, j ) >= heatThreshold then
local goalLength = nil
if doComp then
goalLength = structure.GetDistance( i, j )
if contactmap.IsContact( i, j ) then
strength = CONTACTMAP_CONTACTING_STRENGTH * baseStrength
goalLength = CONTACTMAP_CONTACTING_GOAL * goalLength
else
strength = CONTACTMAP_NONCONTACTING_STRENGTH * baseStrength
goalLength = CONTACTMAP_NONCONTACTING_GOAL* goalLength
end
end
if ditherValues then
strength = random( 0.95, 1.05, true ) * strength
goalLength = random( 0.95, 1.05, true ) * goalLength
end
changeSucceeded = BandBetweenSegsWithParameters( i, j, strength, goalLength, atom1, atom2 )
or changeSucceeded
end
end
end
return changeSucceeded
end
function PutPushPullContactMapBands(
badSeg, bandsWanted,
center, strength, heatThreshold,
wantPushNotPull )
print( "PushPullCM: "..badSeg.." center="..center )
changeSucceeded = false
local okSegList = {}
local SC = structure.GetDistance( badSeg, center )
local SR
for i=1, segCt do
local RC=structure.GetDistance( i, center )
SR=structure.GetDistance( i, badSeg )
if SR<15
and RC<SC-3
and math.abs( i - badSeg ) > 5
and math.abs( i - center ) > 3
then
okSegList[#okSegList + 1] = i
end
end
randomizeIndexList( okSegList )
local bandsAdded = 0
for i=1, #okSegList do
SR=structure.GetDistance( i, badSeg )
if wantPushNotPull then
SR=SR + 4
else
SR=SR - 4
end
if SR>20 then SR=20 end
if SR<3 then SR=3 end
if BandBetweenSegsWithParameters( badSeg, okSegList[i], strength, SR ) then
changeSucceeded = true
bandsAdded = bandsAdded + 1
end
if bandsAdded >= bandsWanted then break end
end -- FOR(i)
return changeSucceeded
end
function PutVoidCrusherContactMapBands( idx, strength, heatThreshold )
print( "VoidCrusherCM at "..idx )
changeSucceeded = false
local t={} -- store possible segments
for b=1,segCt do --test all segments
if math.abs(idx-b) >= 15 and contactmap.GetHeat( idx, b ) >= heatThreshold then
local ab = structure.GetDistance(idx, b)
if ab > 10.0 then --no void if less
local void=true
for c=1,segCt do --searching for any segment between them
if c ~= idx and c ~= b then -- don't want c = idx or b
local ac = structure.GetDistance(idx,c)
local bc = structure.GetDistance(b,c)
if ac<ab and bc<ab and ac>4 and bc>4 then
if ac+bc<ab+1.5
then void=false break --no void there for sure
end
end
end
end -- END for c
if void==true then
t[#t+1]={ idx, b }
end
end -- END if idx and b are spatially at least 10 apart
end -- END if idx and b are count-wise at least 15 apart
end -- LOOP over candidate b's
if #t>0 then
for i=1,#t do
local s=t[i][1]
local e=t[i][2]
local d=structure.GetDistance( s, e )
d = d - 3.0 -- try to compress this much...
if d<3 then d=3 end
if d>20 then d=20 end
if BandBetweenSegsWithParameters( s, e, strength, d ) then changeSucceeded = true end
end
end
return changeSucceeded
end
--------------------------------------------------------------
--
---- MUTATOR FUNCTIONS
-- TODO: Consider adding a "very similar" mutator
--------------------------------------------------------------
-- this one doesn't care what amino used to be at idx
function MutateToAny( idx )
local aa = structure.GetAminoAcid( idx )
local aanew = aa
local i = 0
while ( aanew == aa ) do
i = random( aaCount )
aanew = AA( i )
end
print( "mutating " .. aa .. " at " .. idx .. " to " .. aanew )
structure.SetAminoAcid( idx, aanew )
return true
end
-- this one preserves hydrophob/hydrophil
function MutateToSimilar( idx )
local aa = structure.GetAminoAcid( idx )
local i = 0
local aanew = aa
while (aanew == aa) do
if structure.IsHydrophobic( idx ) then
i = random( aaPhobCount )
aanew = AAphobic( i )
else
i = random( aaPhilCount )
aanew = AAphilic( i )
end
end
print( "mutating " .. aa .. " at " .. idx .. " to " .. aanew )
structure.SetAminoAcid( idx, aanew )
return true
end
----------------------------------------------------------------------
--
---- IDEALIZERS AND REBUILDERS
--
----------------------------------------------------------------------
----------------------------------------------------------------------
-- IDEALIZERS
----------------------------------------------------------------------
function IdealizeInRange( startSeg, endSeg, iters, useCuts )
print( "IdealizeInRange for "..startSeg.." to "..endSeg )
if useCuts == nil then useCuts = true end
local idxList = { }
changeFailed = false
if startSeg > endSeg then startSeg, endSeg = endSeg, startSeg end
if startSeg < 1 then startSeg = 1 end
if endSeg > segCt then endSeg = segCt end
local tries = 0
save.Quicksave( QS_ShortUseTemp1 )
local delta = 0.0
repeat
save.Quickload( QS_ShortUseTemp1 )
changeFailed = false
if IDEALIZE_MAXTRIES > 1 and tries == IDEALIZE_MAXTRIES - 1 then
delta = 0.0
useCuts = false -- overrule the user's request for use of cuts (should always succeed)
end
if useCuts then
if startSeg - 2 > 1 then
structure.InsertCut( startSeg - 2 )
end
if endSeg + 1 < segCt then
structure.InsertCut( endSeg + 1 )
end
if startSeg - 3 >= 1 then
BandBetweenSegsWithParameters( startSeg-3, startSeg, 1.0 + delta,
structure.GetDistance( startSeg-3, startSeg ) )
end
if endSeg + 3 <= segCt then
BandBetweenSegsWithParameters( endSeg, endSeg + 3, 1.0 + delta,
structure.GetDistance( endSeg, endSeg+3 ) )
end
end
selection.DeselectAll( )
getSegsWithinRangeOfTube( idxList, startSeg, endSeg, IDEALIZE_TUBE_RADIUS )
for i=1, #idxList do selection.Select( idxList[i] ) end
structure.IdealizeSelected( )
selection.DeselectAll( )
if useCuts then
-- get some wiggling happening
print( "WiggleZlbNicely" )
WiggleZlbNicely( iters, IDEALIZE_ZLB_STRENGTH + delta,
math.max( 1, startSeg-2 ), math.min( segCt, endSeg+2 ) )
-- now put the protein back in its proper shape
if startSeg - 2 > 1 then
if not DeleteCutAtIndex(startSeg - 2 ) and IDEALIZE_FORBID_UNHEALED_CUTS then
changeFailed = true
end
end
if changeFailed == false and endSeg + 1 < segCt then
if not DeleteCutAtIndex(endSeg + 1 ) and IDEALIZE_FORBID_UNHEALED_CUTS then
changeFailed = true
end
end
else -- since we didn't put in any cuts, we can try simple wiggling to see how we did
print( "WiggleRange" )
recentbest.Save( )
WiggleRange( iters, startSeg - 2, endSeg + 1, false, random( ) < 0.50 )
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
recentbest.Restore( )
end
if not changeFailed then
DelBands( )
return true
end
tries = tries + 1
delta = delta + 0.25
until tries >= IDEALIZE_MAXTRIES
DelBands( )
return false
end
----------------------------------------------------------------------
-- REBUILDERS
----------------------------------------------------------------------
function RebuildInRange( startSeg, endSeg, maxTries, maxFall )
if maxFall == nil then maxFall = REBUILD_MAX_MEDIUM_SCOREDROP end
print( "RebuildInRange from "..startSeg.." to "..endSeg )
changeSucceeded = false
local iters = QUICKWIGGLE_ITERS
local oldScoreMat = {}
local locss = {}
local killedSS = false
local startScore = getQuickScore( )
ConstructScoreMatrix( oldScoreMat )
-- probably PickRebuild should decide whether to do secondary structure stuff
for i=startSeg, endSeg do locss[i] = structure.GetSecondaryStructure( i ) end
for i=startSeg, endSeg do
if structure.GetSecondaryStructure( i ) == 'E' then
killedSS = true
structure.SetSecondaryStructure( i, 'L' )
end
end
selection.DeselectAll( )
save.Quicksave( QS_ShortUseTemp1 ) -- holds our "running" pose
save.Quicksave( QS_ShortUseTemp2 ) -- holds our "best so far" result
bestRescore = -10000.0
for i=1, maxTries do
local idxListInner = { }
save.Quickload( QS_ShortUseTemp1 ) -- bring up our "running" pose
selection.SelectRange( startSeg, endSeg )
RebuildSelected( i ) -- not only up to maxTries times, but up to maxTries iterations per time
selection.DeselectAll( )
if math.abs( getQuickScore( ) - startScore ) >= 0.01 then
-- give the whole thing a little bit of shake-and-wiggle-time (deliberately not complete)
structure.WiggleSelected( iters )
getSegsWithinRangeOfTube( idxListInner, startSeg, endSeg, REBUILD_INNER_TUBE_RADIUS )
ShakeOrMutate( SHAKESIDECHAIN_ITERS, idxListInner ) -- only shake the "nearby" segments
structure.WiggleAll( iters, true, true ) -- just a short bit of time on this wiggle to settle things down a bit
rescore = getQuickScore( )
if rescore > startScore then -- we have a real improvement!
save.Quicksave( QS_ShortUseTemp1 ) -- replace our "running" pose with this better one
save.Quicksave( QS_ShortUseTemp2 ) -- our best-so-far is better
startScore = rescore
bestRescore = rescore
changeSucceeded = true
else -- not improved, but maybe it's worth keeping anyway?
if rescore > bestRescore and IsNeighborDifferentFromOld( oldScoreMat, 0.1 ) then
save.Quicksave( QS_ShortUseTemp2 ) -- our best-so-far is better
bestRescore = rescore
if startScore - rescore < maxFall then changeSucceeded = true end
end
end
end
end
save.Quickload( QS_ShortUseTemp2 )
if killedSS then
for i=startSeg, endSeg do structure.SetSecondaryStructure( i, locss[ i ] ) end
end
selection.DeselectAll( )
return changeSucceeded
end
function RebuildSection_Quaker( startSeg, endSeg, maxTries, maxFall )
print( "RebuildSection_Quaker from "..startSeg.." to "..endSeg )
local initScore = getQuickScore( )
local tries = 0
repeat
selection.DeselectAll( )
selection.SelectRange( startSeg, endSeg )
tries = tries + 1
RebuildSelected( tries )
newScore = getQuickScore( )
until newScore ~= initScore or tries > maxTries
if newScore == initScore then
return false -- we failed
end
recentbest.Save( ) -- N.B. This is exactly the sort of situation recentbest was made for!
RebuildSelected( maxTries - 1 ) -- maybe use REBUILD_MAXITERS - 1 ?
recentbest.Restore( )
WiggleAndShakeForRebuilder( initScore, maxFall )
-- quaking rebuild put a perform-quake-if-score-not-better-but-not-too-bad here
return true
end
function RebuildSimple( idx, len )
print( "RebuildSimple for ".. idx.. " to "..idx + len - 1 )
local initscore = getQuickScore( )
local idxList = { idx }
for i=1, len - 1 do
if idx + i <= segCt then idxList[ #idxList + 1 ] = idx + i
else break
end
end
selection.DeselectAll( )
selection.SelectRange( idx, math.min( segCt, idx + len - 1 ) )
RebuildSelected( 1 )
if getQuickScore( ) == initscore then return false end
recentbest.Save( )
ShakeOrMutate( SHAKESIDECHAIN_ITERS, idxList )
recentbest.Restore( )
structure.IdealizeSelected( )
recentbest.Restore( )
ShakeOrMutate( SHAKESIDECHAIN_ITERS, idxList )
recentbest.Restore( )
structure.WiggleAll( 25 )
recentbest.Restore( )
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
recentbest.Restore( )
IterativeWiggleAll( QUICKWIGGLE_ITERS, QUICKWIGGLE_GAIN, true, true )
recentbest.Restore( )
return true
end
function RebuildSimple2( idx, len )
print( "RebuildSimple2 for ".. idx.. " to "..idx + len - 1 )
local initscore = getQuickScore( )
selection.DeselectAll( )
selection.SelectRange( idx, idx + len - 1)
RebuildSelected( 1 )
if getQuickScore( ) == initscore then return false end
recentbest.Save( )
SetCI( 0.5 )
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
SetCI( 1.0 )
structure.WiggleAll( 15 )
recentbest.Restore( )
SetCI( 0.5 )
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
SetCI( 1.0 )
IterativeWiggleAll( QUICKWIGGLE_ITERS, QUICKWIGGLE_GAIN, true, true )
recentbest.Restore( )
return true
end
function RebuildSimple3( idx, len, addIdealize )
print( "RebuildSimple3 for ".. idx.. " to "..idx + len - 1 )
local initscore = getQuickScore( )
selection.DeselectAll( )
selection.SelectRange( idx, idx + len - 1)
RebuildSelected( 4 )
if getQuickScore( ) ~= initscore then
recentbest.Save( )
if addIdealize then structure.IdealizeSelected( ) end
VerySimpleNonBandedWiggler( QUICKWIGGLE_ITERS )
recentbest.Restore( )
return true
end
return false
end
function WiskyRebuildAllAction( )
SelectAllMovable( )
RebuildSelected( 1 )
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
structure.WiggleAll( 10 )
ShakeOrMutate( SHAKESIDECHAIN_ITERS )
IterativeWiggleAll( QUICKWIGGLE_ITERS, QUICKWIGGLE_GAIN, true, true )
end
----------------------------------------------------------------------
-- OPERATION-SPECIFIC CHOOSING FUNCTIONS
----------------------------------------------------------------------
function PickRebuildRangeAction( startSeg, endSeg, maxFall )
local iters = QUICKWIGGLE_ITERS
changeSucceeded = false
if random( ) < 0.50 then
algName = "RebuildInRange"
local initScore = getQuickScore( )
changeSucceeded = RebuildInRange( startSeg, endSeg, REBUILD_TRYCOUNT, maxFall )
if changeSucceeded then
if REBUILD_ADDIDEALIZE then
DebugPrint("Putting Idealize after rebuild")
save.Quicksave( QS_ShortUseTemp2 ) -- so we can throw out the idealize if we don't like result
local score = getQuickScore( )
local check = IdealizeInRange( startSeg, endSeg, iters, REBUILD_USECUTS )
if check and score < getQuickScore( ) then
save.Quicksave( QS_ShortUseTemp2 )
score = getQuickScore( )
end
save.Quickload( QS_ShortUseTemp2 )
end
local idxListOuter = {}
getSegsWithinRangeOfTube( idxListOuter, startSeg, endSeg, REBUILD_OUTER_TUBE_RADIUS )
IterativeWiggleShakeWiggle( idxListOuter, iters )
end
else
-- this one comes with its own wiggler and skaker built-in
algName = "RebuildSectionQ" -- we'll just overrule alg's name choice for the time being
changeSucceeded = RebuildSection_Quaker( startSeg, endSeg, 8, maxFall ) -- 8 => REBUILD_TRYCOUNT ?
end
return changeSucceeded
end
function PickRebuildPointAction( idx, maxRange, maxFall )
if maxFall == nil then maxFall = REBUILD_MAX_MEDIUM_SCOREDROP end
if maxRange == nil then maxRange = math.floor( REBUILD_MAX_SEGLEN / 2 ) end
local startIdx = math.max( 1, idx - random( maxRange ) )
local endIdx = math.min( segCt, idx + random( maxRange ) )
for i=idx, endIdx do
if not IsMovableSeg(i) then
endIdx = i - 1
break
end
end
for i=idx, startIdx, -1 do
if not IsMovableSeg(i) then
startIdx = i + 1
break
end
end
return PickRebuildRangeAction( startIdx, endIdx, maxFall )
end
function PickRebuildSimpleAction( idx )
if idx == nil or idx == 0 then idx = randomMovableSeg( ) end
return RebuildSimple( idx, 1 )
end
function PickRebuildSimple2Action( idx )
if idx == nil or idx == 0 then idx = randomMovableSeg( ) end
return RebuildSimple2( idx, 1 )
end
function PickSimpleRebuildRangeAction( idx )
if idx == nil then idx = randomMovableSeg( ) end
local iters = QUICKWIGGLE_ITERS
local len = random( REBUILD_MAX_SEGLEN )
if idx + len - 1 > segCt then len = segCt - idx + 1 end
return RebuildSimple3( idx, len, random( ) < 0.5 )
end
function PickContactMapRebuildRangeAction( idx )
if idx == nil then idx = randomMovableSeg( ) end
print( "ContactMapRebuildRange at ".. idx )
local iters = QUICKWIGGLE_ITERS
changeSucceeded = PickContactMapper( )
if changeSucceeded then
local len = random( REBUILD_MAX_SEGLEN )
if idx + len - 1 > segCt then len = segCt - idx + 1 end
if USE_LOWCI_WIGGLES then
changeSucceeded = RebuildSimple3( idx, len, random( ) < 0.50 )
elseif random( ) < 0.50 then
changeSucceeded = RebuildSimple( idx, len )
else
changeSucceeded = RebuildSimple2( idx, len )
end
end
DelBands()
if changeSucceeded then qStabWiggle( iters, random() < PROB_QSTAB_EXTRA ) end
return changeSucceeded
end
-- think about all-loop and restoring secondary structure and such
function PickContactMapRebuildSSAction( idx )
if idx == nil then idx = randomMovableSeg( ) end
print( "Performing PickContactMapRebuildSSAction for "..idx )
local iters = QUICKWIGGLE_ITERS
local oldInnerTube = REBUILD_INNER_TUBE_RADIUS
local oldOuterTube = REBUILD_OUTER_TUBE_RADIUS
REBUILD_INNER_TUBE_RADIUS = REBUILD_CM_INNER_TUBE_RADIUS
REBUILD_OUTER_TUBE_RADIUS = REBUILD_CM_OUTER_TUBE_RADIUS
changeSucceeded = PickContactMapper( )
if changeSucceeded then
local sIdx, eIdx = GetRegionForOperation( idx )
if random( ) < 0.50 then
changeSucceeded = RebuildInRange( sIdx, eIdx,
REBUILD_TRYCOUNT,
REBUILD_MAX_MEDIUM_SCOREDROP )
else
changeSucceeded = RebuildSection_Quaker( sIdx, eIdx,
REBUILD_TRYCOUNT,
REBUILD_MAX_MEDIUM_SCOREDROP )
end
end
REBUILD_INNER_TUBE_RADIUS = oldInnerTube
REBUILD_OUTER_TUBE_RADIUS = oldOuterTube
DelBands()
if changeSucceeded then qStabWiggle( iters, random() < PROB_QSTAB_EXTRA ) end
return changeSucceeded
end
function PickContactMapRebuildAllAction( )
print( "PickContactMapRebuildAllAction" )
local iters = QUICKWIGGLE_ITERS
changeSucceeded = PickContactMapper( )
if changeSucceeded then
WiskyRebuildAllAction( )
end
DelBands()
if changeSucceeded then qStabWiggle( iters, true ) end
return changeSucceeded
end
function PickIdealizeRangeAction( idx )
if idx == nil then idx = randomMovableSeg( ) end
print( "PickIdealizeRangeAction for ".. idx )
local iters = QUICKWIGGLE_ITERS
local maxSeg = math.min( segCt, idx + random( IDEALIZE_MAX_SEGLEN ) )
local startIdx, endIdx = GetRegionForOperation( idx )
if endIdx - startIdx + 1 > IDEALIZE_MAX_SEGLEN then
if idx - startIdx + 1 > IDEALIZE_MAX_SEGLEN then
startIdx = math.max( 1, idx - IDEALIZE_MAX_SEGLEN)
end
end
if endIdx - startIdx + 1 > IDEALIZE_MAX_SEGLEN then
endIdx = startIdx + IDEALIZE_MAX_SEGLEN - 1
end
changeSucceeded = IdealizeInRange( startIdx, endIdx, iters, IDEALIZE_USE_CUTS )
if changeSucceeded then qStabWiggle( iters, random( ) < PROB_QSTAB_EXTRA ) end
return changeSucceeded
end
function PickIdealizePointAction( idx )
if idx == nil then idx = randomMovableSeg( ) end
print( "PickIdealizePointAction for ".. idx )
local iters = QUICKWIGGLE_ITERS
changeSucceeded = IdealizeInRange( idx, idx, iters, IDEALIZE_USE_CUTS )
if changeSucceeded then qStabWiggle( iters, random( ) < PROB_QSTAB_EXTRA ) end
return changeSucceeded
end
function PickSimpleIdealizeAction( idx )
if idx == nil then idx = randomMovableSeg( ) end
print( "SimpleIdealize for segment " .. idx )
local iters = QUICKWIGGLE_ITERS
local maxSeg = math.min( segCt, idx + random( IDEALIZE_MAX_SEGLEN ) )
selection.DeselectAll( )
selection.SelectRange( idx, maxSeg )
structure.IdealizeSelected( )
selection.DeselectAll( )
VerySimpleNonBandedWiggler( iters )
return true
end
function PickSingleBandInSpace( idx )
if idx == nil then idx = randomUnlockedSeg( ) end
local iters = QUICKWIGGLE_ITERS
local strength = random( BAND_STRENGTH_DEFAULT / 2, BAND_STRENGTH_DEFAULT, true )
-- print is done here because PutBandInSpace is called by other pickers
print( "BIS for segment " .. idx.." with strength "..TrimNum( strength ) )
changeSucceeded = PutBandInSpace( idx, BIS_LENGTH, strength )
if changeSucceeded then -- must keep wiggler in-house, lowci isn't useful
RegionWiggleBeforeAndAfter( iters, idx, idx )
end
return changeSucceeded
end
function PickBandToRandomSeg( idx )
if idx == nil then idx = randomUnlockedSeg( ) end
local strength = BAND_STRENGTH_DEFAULT
local atom = PickAtomNumberForBand( idx )
-- print is done here because PutBandToRandomSeg is called by other pickers
print( "BandBetween for segment " .. idx.." with strength "..TrimNum( strength ) )
return PutBandToRandomSeg( idx, strength, atom )
end
function PickGlycineHinge( idx )
if idx == nil then idx = randomAASeg( ) end
if idx == 0 then return false end
local strength = random( 0.25, BAND_STRENGTH_DEFAULT, true )
return PutGlycineHingeBands( idx, strength )
end
function PickGeneralHingeBands( idx )
if idx == nil then idx = randomMovableSeg( ) end
local strength = BAND_STRENGTH_REGION
local bisLen = BIS_LENGTH
local startSeg, endSeg = GetRegionForOperation( idx )
if endSeg - startSeg <= 1 then return false end -- something's very wrong
if startSeg < idx - 2 then startSeg = idx - random( 0, 2 ) end
if endSeg > idx + 2 then endSeg = idx + random( 0, 2 ) end
return PutGeneralHingeBands( startSeg, endSeg, strength )
end
function PickPushPullAction( idx )
if idx == nil then idx = randomUnlockedSeg( ) end
local segList = {}
local wantPushNotPull = random( ) < 0.50
local ctBands = 5
local strength = BAND_STRENGTH_DEFAULT
getSegsOutsideSphere( segList, idx, 8 )
if #segList == 0 then
-- nothing we can do!
print(" PushPull failed due to lack of distant segs")
return false
end
local centerSeg = segList[ random( #segList) ]
return PutPushPullBands( idx, ctBands, centerSeg, strength, wantPushNotPull )
end
function PickVoidCrusherAction( idx )
if idx == nil then idx = randomUnlockedSeg( ) end
local strength = BAND_STRENGTH_DEFAULT
return PutVoidCrusherBands( idx, strength )
end
function PickRotatorBands( idx )
if idx == nil then idx = randomMovableSeg( ) end
local iters = QUICKWIGGLE_ITERS
local strength = BAND_STRENGTH_REGION
local bisLen = BIS_LENGTH
local goClockwise = random( ) < 0.50
local startSeg, endSeg = GetRegionForOperation( idx )
if endSeg - startSeg <= 1 then return false end -- something's very wrong
changeSucceeded = PutRotatorBands( startSeg, endSeg, bisLen, strength, goClockwise )
if changeSucceeded then -- must keep wiggler in-house, lowci isn't useful
RegionWiggleBeforeAndAfter( iters, startSeg, endSeg )
end
DelBands( )
return changeSucceeded
end
function PickPusherBands( idx )
if idx == nil then idx = randomMovableSeg( ) end
local iters = QUICKWIGGLE_ITERS
local strength = BAND_STRENGTH_REGION
local bisLen = BIS_LENGTH
local goForward = random( ) < 0.50
local startSeg, endSeg = GetRegionForOperation( idx )
if endSeg - startSeg <= 1 then return false end -- something's very wrong
changeSucceeded = PutPusherBands( startSeg, endSeg, bisLen, strength, goForward )
if changeSucceeded then -- must keep wiggler in-house, lowci isn't useful
RegionWiggleBeforeAndAfter( iters, startSeg, endSeg )
end
DelBands( )
return changeSucceeded
end
function PickMoverBands( idx )
if idx == nil then idx = randomMovableSeg( ) end
local iters = QUICKWIGGLE_ITERS
local strength = BAND_STRENGTH_REGION
local bisLen = BIS_LENGTH
local startSeg, endSeg = GetRegionForOperation( idx )
if endSeg - startSeg <= 1 then return false end -- something's very wrong
changeSucceeded = PutMoverBands( startSeg, endSeg, bisLen, strength )
if changeSucceeded then -- must keep wiggler in-house, lowci isn't useful
RegionWiggleBeforeAndAfter( iters, startSeg, endSeg )
end
DelBands( )
return changeSucceeded
end
function PickFlexerBands( idx )
if idx == nil then idx = randomMovableSeg( ) end
local iters = QUICKWIGGLE_ITERS
local strength = BAND_STRENGTH_REGION
local bisLen = BIS_LENGTH
local startSeg, endSeg = GetRegionForOperation( idx )
if endSeg - startSeg <= 1 then return false end -- something's very wrong
changeSucceeded = PutFlexerBands( startSeg, endSeg, bisLen, strength )
if changeSucceeded then -- must keep wiggler in-house, lowci isn't useful
RegionWiggleBeforeAndAfter( iters, startSeg, endSeg )
end
DelBands( ) -- only relevant if PutFlexerBands failed
return changeSucceeded
end
function PickSheetStraightenerBands( idx )
if idx == nil then idx = randomMovableSeg( ) end
local strength = BAND_STRENGTH_REGION
local startSeg, endSeg = GetRegionForOperation( idx )
if endSeg - startSeg <= 1 then return false end -- something's very wrong
return PutSheetStraightenerBands( startSeg, endSeg, strength )
end
function PickHelixUniformBands( idx )
if idx == nil then idx = randomMovableSeg( ) end
local strength = BAND_STRENGTH_REGION
local startSeg, endSeg = GetRegionForOperation( idx )
if endSeg - startSeg <= 1 then return false end -- something's very wrong
local regionLength = endSeg - startSeg + 1
local hShift = 4
if random( ) < 0.25 then
hShift = math.min( regionLength - 2, randomDice( 4, 1, 2 ) ) -- a number 4 - 8, preferring 6ish
end
if startSeg < idx - 2 then startSeg = idx - random( 0, 2 ) end
if endSeg > idx + 2 then endSeg = idx + random( 0, 2 ) end
return PutHelixUniformDistanceBands( startSeg, endSeg, hShift, strength )
end
function PickHelixCompressorBands( idx )
if idx == nil then idx = randomMovableSeg( ) end
local strength = BAND_STRENGTH_REGION
local hComp = random( BAND_MINCOMPRESS, BAND_MAXEXPAND )
local startSeg, endSeg = GetRegionForOperation( idx )
if endSeg - startSeg <= 1 then return false end -- something's very wrong
local regionLength = endSeg - startSeg + 1
local hShift = 4
if random( ) < 0.25 then
hShift = math.min(regionLength - 2, randomDice(4, 1, 2)) -- a number 4 - 8, preferring 6ish
end
return PutHelixCompressorBands( startSeg, endSeg, hShift, strength, hComp )
end
function PickHelixFixerBands( idx )
if idx == nil then idx = randomMovableSeg( ) end
local strength = BAND_STRENGTH_REGION
local do4 = random( ) < 0.5
local do5 = random( ) < 0.5
local startSeg, endSeg = GetRegionForOperation( idx )
if endSeg - startSeg <= 1 then return false end -- something's very wrong
return PutHelixFixerBands( startSeg, endSeg, strength, do4, do5 )
end
function PickSheetSewerAction( )
local maxSheetDistance = random( 4.0, 12.0, true ) -- default choice was 8.0
local strength = BAND_STRENGTH_DEFAULT
return PutSheetSewerBandsAllOver( maxSheetDistance, strength )
end
function PickMultiBander( )
local strength = random( 0, maxStrength, true ) + 0.001
local ctBandsToMake = randomDice(3, 1, 4) -- at least 3, maybe as many as 12
local bandCt = 0
print( "MultiBander with bands="..ctBandsToMake )
while bandCt < ctBandsToMake do
local which = random( 2 )
if which == 1 then
local seg = randomUnlockedSeg( )
local maxRho = 1.0
if PutBandInSpace( seg, maxRho, strength ) then
changeSucceeded = true
bandCt = bandCt + 1
end
else
local idx = randomUnlockedSeg( )
local atom = PickAtomNumberForBand( idx )
if PutBandToRandomSeg( idx, strength, atom ) then
bandCt = bandCt + 1
end
end
end
return changeSucceeded
end
function PickCentralBander( )
local strength = BAND_STRENGTH_DEFAULT
local maxCenters = randomDice( 2, 1, 3)
changeSucceeded = false
local coreSegs = {}
getCentralHydrophobics( coreSegs, maxCenters)
local compression = randomDice( 2, BAND_MINCOMPRESS/2, BAND_MAXEXPAND/2 )
local skipCt = random(4, 10)
local startOffset = random(1, skipCt)
return PutCoreCompressBands( coreSegs, skipCt, startOffset, strength,
compression, random( )<0.5, random( )<0.5 )
end
function PickCoreStirrer( )
-- not well suited to a lowCI situation
local iters = QUICKWIGGLE_ITERS
changeSucceeded = false
local coreSegs = {}
getCentralHydrophobics( coreSegs, math.floor(segCt / 8) ) -- for a 100-seg protein, this is about 13
-- perform an expansion
local maxCenters = randomDice( 3, 1, 3 ) -- range 3 to 9, prefers 5-7
local thisCore = {}
local expansion = random( 1.00, BAND_MAXEXPAND )
local strength = BAND_STRENGTH_DEFAULT
local skipCt = random( 3, 13 )
local startOffset = random( 1, skipCt )
local pullPhilics = random( ) < 0.5
local doSidechains = random( ) < 0.5
getCentralHydrophobics( thisCore, maxCenters )
changeSucceeded = PutCoreCompressBands( thisCore, skipCt, startOffset, strength,
expansion, pullPhilics, doSidechains )
if not changeSucceeded then return false end
print( "BandedWiggleUntilEnoughChange" ) -- needs to keep its wiggler in-house
BandedWiggleUntilEnoughChange( iters, getQuickScore( ) / 40.0, COREPULLING_CI )
DelBands( )
--
-- jiggle a bunch of rotamers, chosen from phobics "near" the center.
--
local ctFlips = 0
for i=1, #coreSegs do
local ctRots = rotamer.GetCount( coreSegs[ i ] )
if ctRots > 1 then
rotamer.SetRotamer( coreSegs[ i ], random( ctRots ) )
end
end
qStabWiggle( iters, true ) -- complete action of SimpleBandedWiggleWrapper now!
return true
end
function PickEveryXToEveryYBanding( )
local Xinterval = random( math.floor( segCt / 12), math.floor( segCt / 4 ) )
local Yinterval = random( 2 + Xinterval, 2 + math.floor( segCt / 4 ) )
local Xinit = random( 2*Xinterval )
local Yinit = random( 2*Yinterval )
local strength = random( 0.01, BAND_STRENGTH_DEFAULT, true )
local compression = random( BAND_MINCOMPRESS, BAND_MAXEXPAND, true )
return PutBandsEveryXToEveryY( Xinterval , Xinit, Yinterval, Yinit, strength, compression )
end
function PickGentleCompBander( )
local minLength = random( 9.0, 16.0, true ) -- 10 and 14 seen in scripts
local maxLength = random( 18.0, 32.0, true ) -- 20 and 30 seen in scripts; tempting to do ~2*minLength
local minSeparation = random( 4, 12 ) -- 5 and 10 seen in scripts
local compression = random( BAND_MINCOMPRESS, 0.90, true ) -- 0.70 seen in scripts
local bandsWanted = randomDice( 3, 2, 5 ) -- usually tunable; 7-9 for standalone is typical; 3-5 when added
local strength = random( 0.50, 1.50) * BAND_STRENGTH_DEFAULT
return PutGentleCompBands( bandsWanted, minLength, maxLength,
minSeparation, strength, compression )
end
function PickLSQuaker( )
local idx = randomSeg( ) -- we don't care if it's movable
local wantFreeze = random( ) < 0.50
local ctBands = randomDice( 2, 2, 4 ) -- was 6
local strength = BAND_STRENGTH_DEFAULT
local compression = random( BAND_MINCOMPRESS, BAND_MAXEXPAND ) -- not present before. strength was tuned
ResetFrozenness( )
local startIdx, endIdx = GetRegionForOperation( idx )
print( "LS:PutManyBandsToSeg over range "..startIdx..", "..endIdx )
for i=startIdx, endIdx do
local changeSuc = PutManyBandsToSeg( i, ctBands, strength, compression, true)
changeSucceeded = changeSucceeded or changeSuc
end
if changeSucceeded then
if wantFreeze then
selection.DeselectAll( )
selection.SelectRange( startIdx, endIdx )
ChangedFrozenness = true
freeze.FreezeSelected( true, true )
selection.DeselectAll( )
end
end
return changeSucceeded
end
function PickSingleContactMapBand( idx )
if idx == nil then idx = randomContactableSeg( ) end
print( "PickSingleContactMapBand at ".. idx )
changeSucceeded = false
local heatThreshold = CONTACTMAP_THRESHOLD
local strength = 1.5 * BAND_STRENGTH_DEFAULT
return PutSomeContactMapBands( heatThreshold, strength, 1, random( ) < 0.50 )
end
function PickSomeContactMapBands( )
print( "PickSomeContactMapBands" )
changeSucceeded = false
local heatThreshold = CONTACTMAP_THRESHOLD
local strength = BAND_STRENGTH_DEFAULT
local ctWanted = random( 5, 10 )
return PutSomeContactMapBands( heatThreshold, strength, ctWanted, random( ) < 0.50 )
end
function PickContactMapBandsForSeg( idx )
if idx == nil then idx = randomContactableSeg( ) end
changeSucceeded = false
local heatThreshold = CONTACTMAP_THRESHOLD
local strength = BAND_STRENGTH_DEFAULT
local doSidechain = false
if BAND_TO_SIDECHAINS then
doSidechain = random( ) < 0.50
end
return PutAllContactMapBandsToSeg( idx, heatThreshold, strength, true, doSidechain )
end
function PickCMTwoRegionBander( idx1 )
if idx1 == nil then idx1 = randomContactableSeg( ) end
local strength = BAND_STRENGTH_DEFAULT
local heatThreshold = CONTACTMAP_THRESHOLD
local startIdx1, endIdx1 = GetRegionStartAndEnd( idx1 )
if startIdx1 == 1 and endIdx1 == segCt then return false end -- we just can't deal with this
local idx2 = startIdx1
local tryCount = 0
while idx2 >= startIdx1 and idx2 <= endIdx1 and tryCount < 50 do
for i=startIdx1, endIdx1 do
idx2 = randomContactSeg( i, CONTACTMAP_THRESHOLD )
if idx2 ~= 0 and (idx2 < startIdx1 or idx2 > endIdx1) then break end -- got one
end
tryCount = tryCount + 1
end
if idx2 == 0 then return false end -- we have failed
if tryCount == 50 then return false end
local startIdx2, endIdx2 = GetRegionStartAndEnd( idx2 )
return PutContactMapBandsBetweenRegions( heatThreshold,
startIdx1, endIdx1, startIdx2, endIdx2,
strength, true, random( ) < 0.50, random( ) < 0.50 )
end
function PickContactMapper( )
local threshold = CONTACTMAP_THRESHOLD
local madeStrength = random( 0.96, 1.04, true ) * CONTACTMAP_CONTACTING_STRENGTH
local madeLenScale = random( 0.96, 1.04, true ) * CONTACTMAP_CONTACTING_GOAL
local unmadeStrength = random( 0.96, 1.04, true ) * CONTACTMAP_NONCONTACTING_STRENGTH
local unmadeLenScale = random( 0.96, 1.04, true ) * CONTACTMAP_NONCONTACTING_GOAL
local doSidechains = BAND_TO_SIDECHAINS and random( ) < 0.50
local wantDither = random( ) < 0.50
return PutAllContactMapBands( threshold, madeStrength, madeLenScale,
unmadeStrength, unmadeLenScale,
doSidechains, wantDither )
end
function PickPushPullContactMapAction( badSeg )
if badSeg == nil then badSeg = randomContactableSeg( ) end
local segList = {}
local wantPushNotPull = random( ) < 0.50
local ctBands = 5
local strength = BAND_STRENGTH_DEFAULT
getCMSegsOutsideSphere( segList, badSeg, 8, CONTACTMAP_THRESHOLD )
if #segList == 0 then
-- nothing we can do!
print( " PushPull failed due to lack of distant 'center' segs" )
return false
end
centerSeg = segList[ random( #segList ) ]
return PutPushPullContactMapBands( badSeg, ctBands, centerSeg, strength,
CONTACTMAP_THRESHOLD, wantPushNotPull )
end
function PickVoidCrusherContactMapAction( idx )
if idx == nil then idx = randomContactableSeg( ) end
local strength = BAND_STRENGTH_DEFAULT
return PutVoidCrusherContactMapBands( idx, strength, CONTACTMAP_THRESHOLD )
end
function PickCoreCompression( compression )
local thisCore = {}
local maxCenters = randomDice( 3, 1, 3 ) -- range 3 to 9, prefers 5-7
local strength = BAND_STRENGTH_DEFAULT
local skipCt = random( 3, 13 )
local startOffset = random( 1, skipCt )
local pullPhilics = random( ) < 0.5
local doSidechains = false
if BAND_TO_SIDECHAINS then
doSidechains = random( ) < 0.50
end
getCentralHydrophobics( thisCore, maxCenters)
return PutCoreCompressBands( thisCore, skipCt, startOffset, strength, compression, pullPhilics, doSidechains )
end
function PickCoreSpreader( )
local iters = QUICKWIGGLE_ITERS
local maxBWigTries = 5
changeSucceeded = false
compression = random( 1.05, BAND_MAXEXPAND )
local coreSegs = { }
getCentralHydrophobics( coreSegs, math.floor(segCt / 8) ) -- for a 100-seg protein, this is about 13
changeSucceeded = PickCoreCompression( compression )
if changeSucceeded then
BandedWiggleUntilEnoughChange( iters, BANDEDWIGGLE_TARGET, COREPULLING_CI )
end
DelBands( )
return true
end
function PickStirThenContactMap( )
PickCoreSpreader( )
return PickContactMapper( )
end
function PickLocalQuake( idx )
if idx == nil then idx = randomUnlockedSeg( ) end
print( "LocalQuake at".. idx )
local iters = QUICKWIGGLE_ITERS
local ctBands = randomDice( 2, 2, 4 ) -- was 6
local compression = random( BAND_MINCOMPRESS, BAND_MAXEXPAND )
local strength = BAND_STRENGTH_DEFAULT
save.Quicksave( QS_ShortUseTemp1 )
local bestScore = getQuickScore( )
changeSucceeded = false
local changeSuc = false
local lqIdx = idx
for i=1, 10 do
save.Quickload( QS_ShortUseTemp1 )
local currScore = getQuickScore( )
DelBands( )
changeSuc = PutManyBandsToSeg( lqIdx, ctBands, strength, compression, true )
if changeSuc then
idx2 = math.min( segCt, lqIdx + 3 )
changeSuc = PutManyBandsToSeg( idx2, ctBands, strength, compression, true )
end
if changeSuc then
if USE_LOWCI_WIGGLES then
VerySimpleWiggleBeforeAndAfter( iters )
else
SimpleBandedWiggleWrapper( iters, BANDEDWIGGLE_TARGET, COREPULLING_CI )
end
newScore = getQuickScore( )
if newScore > currScore then save.Quicksave( QS_ShortUseTemp1 ) end
changeSucceeded = true -- we only have to succeed once to count as a success
end
lqIdx = segCt - 2
while lqIdx > segCt - 3 do
lqIdx = randomSeg( )
end
end
save.Quickload( QS_ShortUseTemp1 )
DelBands( )
return changeSucceeded
end
function PickMutatorAlgorithm( idx )
if idx == nil then idx = randomMovableSeg( ) end -- better would be random mutable seg
print( "SpotMutator for segment " .. idx )
r = random( )
if r < PROB_MUTATESIMILAR then
return MutateToSimilar( idx )
else
return MutateToAny( idx )
end
end
--------------------------------------------------------------------------
-- SETUP and CLEANUP
--------------------------------------------------------------------------
function CleanPuzzleState( )
ExactSetCI( InitialClashImportance )
selection.DeselectAll( )
ResetFrozenness( )
ResetCuts( )
DelBands( )
ResetSecondaryStructures( 1, segCt )
end
function PrintState( )
local gain = getScore( ) - InitialScore
if gain < 0.001 then
print( " No change" )
else
print( " Startscore: "..TrimNum( InitialScore ))
print( " Score: "..TrimNum( getScore( ) ))
print( " Total gain: "..TrimNum( gain ) )
end
print( " Run time: "..os.time( ) - StartTime )
end
function End( errstr )
if EndCalled then return end -- no infinite recursion please
EndCalled = true
print( "" )
if errstr ~= nil then
if string.find( errstr, "Cancelled" ) then
print( "User cancel" )
else
print( errstr )
end
end
PrintState( )
if SHOW_STATS_REPORT then
print("")
ReportStats( )
print( "TotalShakeTime="..TotalShakeTime ) -- because I want to know.
end
if KILLING_FILTERS then ResetSlowFilterState( ) end
if RESTORE_BEST_AT_END then LoadBest( ) end
if PERFORM_CLEANUP_AT_END then CleanPuzzleState( ) end
end
function InitializePuzzleState( )
seedRandom( )
InitialScore = getScore( )
CurrentBestScore = InitialScore
CurrentBestCmScore = InitialScore
save.Quicksave( QS_Start )
save.Quicksave( QS_CmBest )
save.Quicksave( QS_Best )
StartTime = os.time( )
-- learn a few things
InitialClashImportance = behavior.GetClashImportance( )
SCALE_MAXCI = InitialClashImportance
PuzzleAllowsMutate = IsPuzzleMutable( )
if PuzzleAllowsMutate then PuzzleIsDesigner = checkAla( ) end
PuzzleUsesFilters = CheckForSlowFilters( )
KILLING_FILTERS = false -- PuzzleUsesFilters
CheckForContactMap( )
if PuzzleHasContactMap then InitializeContactMapSegList( CONTACTMAP_THRESHOLD ) end
CurrentBestCmTotal = GetContactScore( ) -- has to wait until after InitializeContactMapSegList
StoreSecondaryStructure( )
SaveFrozenness( )
FindAllMovableSegs( )
getHelices( )
getSheets( )
getLoops( )
DelBands( )
selection.DeselectAll( )
recentbest.Save( ) -- do this after DelBands
end
--------------------------------------------------------------------------
-- MAIN
--------------------------------------------------------------------------
function main( )
InitializePuzzleState()
didSomething = false
print( "Start is in qs="..QS_Start.."; Best is in "..QS_Best.."; CM-best is in "..QS_CmBest )
------------------------DEBUG STUFF
-- if any actions could stand some debugging, put them here
-- then multi-line comment from below to end of function
------------------------
while LetUserChooseParams( ) do
if KILLING_FILTERS then TurnOffSlowFilters( ) end
PerformOperation( OperationChosen )
didSomething = true
LoadBest( )
PrintState( )
end
print( " Done!" )
if didSomething then End( ) end
end
xpcall( main, End )