Icon representing a recipe

Recipe: Simulated Annealing 4.1

created by KarenCH

Profile


Name
Simulated Annealing 4.1
ID
103607
Shared with
Public
Parent
None
Children
None
Created on
June 15, 2020 at 22:19 PM UTC
Updated on
June 15, 2020 at 22:19 PM UTC
Description

Lots of actions controlled by a Simulated Annealing algorithm for choosing which are to be accepted

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. -- -- TODO: -- 1. Cysteine: -- Include band-two-cysteines together (locate closest ones, match together, etc). -- Consider throwing out actions that break connections. -- 2. Rebuild/remix: -- Rebuild with compressor bands. -- Offer remix -- 3. Idealize: -- Compare with cuts versus without, see if lengths should vary. -- Maybe put compressor bands instead of zlb? -- 4. Contactmaps: -- Try more actions such as "put band(s)" + rebuild at low ci; use ZLB-wiggler. -- Try a "Til contact is made" banded wiggler. -- TEST comp-band mods: are they working propery? -- 5. LowCI: -- Should it include extremely lowCI stuff? (mainly idealizing with ZLBs at 0.05) -- 6. Banders: -- Experiment more with expanding the protein before actions. rebuild? late stage? -- 7. Dialog boxes: -- Missing mutate-related options - particularly a no-mutate list, maybe PROB_MUTATESIMILAR -- Missing individual actions: wiggles, banders, rebuilders, idealizers (really want this???) -- USE dlg.foo = dialog.AddTextbox( string label, string value ) -- for no-mutate list from user or cysteine-banders -- Parse value to learn what user wants. -- -- 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+ -- (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 -- (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) -- -------------------------------------------------------------------------------- ---------------------------------------------------------------------- -- ---- SIMULATED-ANNEALING-SPECIFIC STUFF -- ---------------------------------------------------------------------- -- Simulated Annealing "core" values -- The three main phases each perform 2 SA runs. SA_NUMSTARTS_EARLY = 100 -- no expander SA_STEPSPERRUN_EARLY = 15 SA_NUMSTARTS_EARLY2 = 70 -- some expander SA_STEPSPERRUN_EARLY2 = 15 SA_NUMSTARTS_REBUILD = 100 SA_STEPSPERRUN_REBUILD = 15 SA_NUMSTARTS_REBUILD2 = 75 SA_STEPSPERRUN_REBUILD2= 20 -- try for longer session 2nd go-round SA_NUMSTARTS_MID = 1 -- long battle for a few points. SA_STEPSPERRUN_MID = 300 SA_NUMSTARTS_MID2 = 1 -- ugly struggle for a few points. SA_STEPSPERRUN_MID2 = 300 -- when and how often to pre-expand protein before acting on it ALLOW_EXPAND_FOR_ACTION = true PROB_USE_EXPANDER_EARLY2 = 0.25 -- find out what is best PROB_USE_EXPANDER_REBUILD = 0.25 -- find out what is best PROB_USE_EXPANDER_MID1 = 0.5 -- find out what is best PROB_USE_EXPANDER_MID2 = 1.0 -- seems always good here PROB_USE_EXPANDER_LATE = 1.0 -- seems always good here PROB_USE_EXPANDER = PROB_USE_EXPANDER_EARLY2 -- will get set to action's particular choice -- SA-Midgame, how often should it target "bad" indices? PROB_TARGETED = 0.25 -- values for mutate PROB_MUTATESIMILAR = 0.25 -- was 0.75 ------------ LESS INTERESTING SA STUFF ------------ -- constants for weird SA operations SA_STEPSPERRUN_LOW20 = 50 -- CI <= 0.20 -- first half of "LowCI" SA_NUMSTARTS_LOW20 = 4 SA_STEPSPERRUN_LOW50 = 50 -- CI <= 0.50 -- second half of "LowCI" SA_NUMSTARTS_LOW50 = 4 SA_STEPSPERRUN_CMAP = 50 -- special Contact map operations SA_NUMSTARTS_CMAP = 4 SA_STEPSPERRUN_LATE = 50 -- Does fuse. rarely useful SA_NUMSTARTS_LATE = 1 -- values for controlling behavior of fusing during Late stage ALLOW_FUSE = true -- dialog boxes set false for all but OperationChosen=="SA-Late" FUSEWIGGLE_ITERS = 2 FUSEWIGGLE_GAIN = 0.10 PROB_FUSEWIGGLE_CUTFUSE = 0.70 PROB_FUSEWIGGLE_LOCAL = 0.00 -- very slow, very poor success rate PROB_FUSEWIGGLE_BLUEFUSE = 0.30 -- when to bail if a protein is mostly locked and therefore rebuilds always fail MAX_REBUILD_FAILS = 20 rebuild_fails = 0 PERFORM_CYSTEINE_BANDING = false CYSTEINE_BAND_STRENGTH = 5.0 -- 9.0 seems dicy; 5.0 has worked just fine in past MAX_RI_CYSTEINE_SEPARATION = 15.0 -- if more, then pose is nuts. fail the action that did this COUNT_CYSTEINE_BANDS_USED = 3 -- pick 1..4 or break your script -- if a longish run is being done with non-Zero temperature function, this is a way to bail out SA_MINGAIN = 5.0 -- the amount of gain that must be exceeded to not bail out SA_FAILIFNOGAIN = 25 -- the step to quit at -------------------------------------------------------- -- ---------------- BOILERPLATE --------------------- -- -------------------------------------------------------- ------------------------------------------------------------------------------------------------ -- Standard variables/constants used in my foldit scripts. Some are intended to be changed -- as part of setup of scripts, others are user settable, yet more are unlikely to be changed. -- A separate group, indicated as such, are genuinely intended as constants or as program- -- controlled variables. ------------------------------------------------------------------------------------------------ -- 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 EXPAND_BEFORE_ACTION = false -- simple bander options BAND_STRENGTH_DEFAULT = 1.0 BAND_STRENGTH_REGION = 1.0 BAND_MINCOMPRESS = 0.80 BAND_MINEXPAND = 1.05 BAND_MAXEXPAND = 1.20 BANDEDWIGGLE_TARGET = 500.0 BIS_LENGTH = 5.0 -- max length of a normal BIS BIS_LENGTH_LONG = 8.0 -- max length of a "long" 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 USEITERATIVELONGWIGGLE = FALSE LONGWIGGLE_ITERS = 12 -- was 10. 14 too slow. consider 10 for early, 12 for rebuild, 14 for midgame? QUICKWIGGLE_ITERS = 2 QUICKWIGGLE_GAIN = 0.50 -- for iterative wiggles that happen in quickwiggle time PROB_QSTAB_EXTRA = 0.50 PROB_LOCALWIGGLE = 1.00 -- I currently believe "before" wiggles should always prefer LocalWiggle over Wiggle. USE_LOWCI_WIGGLES = false -- set true by default in low-ci scripts -- values for controlling how mutations are handled (under-tested) ALLOW_SPOT_MUTATION = false -- this is for performing local "spot" changes MUTATE_NOT_SHAKE = false -- this is for choosing between shake and mutate -- 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_TUBE_BIG_RADIUS = 10.0 IDEALIZE_MAX_SEGLEN = 5 -- values associated with rebuilding REBUILD_FAILSBEFOREQUIT = 75 REBUILD_TRYCOUNT = 5 REBUILD_ADDIDEALIZE = true REBUILD_USECUTS = true REBUILD_MAX_SEGLEN = 10 -- prefer even! REBUILD_MAX_EARLY_SCOREDROP = 3000.0 REBUILD_MAX_MEDIUM_SCOREDROP = 500.0 REBUILD_MAX_LATE_SCOREDROP = 100.0 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 -- Clash Importance SCALE_MAXCI = 1.0 COREPULLING_CI = 0.70 REGIONMOD_CI = 0.10 -- values for contact map reading CONTACTMAP_THRESHOLD = 0.25 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 MAX_ALLOWED_NONCONTACT = 10.0 -- longer means disbelieve it's a valid contact -- TailWhip constant MAX_IDX_FROM_END = 5 ---------------------------------------------------------------------------------- ------------ VALUES USERS SHOULD NOT BE TOUCHING --------------------------------- ---------------------------------------------------------------------------------- -------------- CONSTANTS THAT PROBABLY SHOULD NEVER CHANGE -------------- -- atoms in an amino acid BETA_CARBON = 5 TERMINAL_BETA = 6 CENTER_CARBON = 2 --this is the default atom for bands to attach to -- ideal distances HELIX_DISTANCE_SKIP4 = 6.3 -- for skips of 4 HELIX_DISTANCE_SKIP5 = 8.6 -- for skips of 5 DISTANCE_SHEET_TO_SHEET = 4.8 DISTANCE_HELIX_TO_THING = 8.2 DISTANCE_SHEET_SEG_TO_SEG = 6.8 DISTANCE_INSHEET_N_TO_NPLUS2 = 9.5 DISTANCE_CYSTEINE_BOND = 2.05 SCALE_CYSTEINE_OFF_1 = 1.8 -- QUICKSAVE slots used herein QS_Start = 1 QS_CmBest = 2 QS_Best = 3 QS_Alternate1 = 4 -- these are for scripts that offer alternate poses QS_Alternate2 = 5 QS_Alternate3 = 6 QS_Alternate4 = 7 QS_Alternate5 = 8 -- a quickslot stack for any system that wants such (some rebuilder/remixer scripts want) QS_Base = 9 -- the base of a stack of slots to operate with QS_Top = QS_Base -- the current top of the stack QS_MAX = 49 -- the max top point for the stack QS_RemixSlot1 = 50 -- surely nobody will remix more than 20 slots! QS_RemixSlotLast = 69 QS_MultiStartSave1 = 70 -- surely nobody will multistart more than 20 times! QS_MultiStartSaveLast = 89 QS_Swapslot = 91 -- if sorting of quickslots is needed, use for swapping QS_Swapslot2 = 92 -- if sorting of quickslots is needed, use for swapping QS_ShortUseTemp1 = 93 -- used by low-level actions that stash state for short time QS_ShortUseTemp2 = 94 -- used by low-level actions that stash state for short time -- following slots are not safe to borrow if MultiTry or SA are in play QS_NeighborTempM1= 95 -- used by MultiTry's "Neighbor Generation" subroutines QS_TopLevelTempMT= 96 -- used by MultiTry top-level manager QS_TopLevelTempM2= 97 -- used by MultiTry top-level manager QS_NeighborTempSA= 98 -- used by SA's "Neighbor Generation" subroutines QS_TopLevelTempSA= 99 -- used by SA top-level manager --------------- 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 ContactMapScore = 0.0 InitialBandCount = band.GetCount( ) CysteineBandPairCount = 0 InitialClashImportance = behavior.GetClashImportance() HasContactMapSegList = {} InitialFrozenTable = {} IsSlowFilterDisabled = behavior.GetSlowFiltersDisabled( ) -- Usage of following tables: bounds = allHelices[i] use bounds.sIdx and bounds.eIdx allHelices = { {sIdx = 0, eIdx = 0} } allSheets = { {sIdx = 0, eIdx = 0} } allLoops = { {sIdx = 0, eIdx = 0} } possibleHelices = { {sIdx = 0, eIdx = 0} } cysteineSegs = {} cysteinePairs = {} NonLockedSegList = {} SecStructList = {} segCt = structure.GetCount( ) MinUnlockedSeg = 1 MaxUnlockedSeg = segCt NoMutateList = {} ShakeAfterMutateList = {} -- generated from NoMutateList, depending on options wanted by user ---------------- 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 ---------------------------------------------------------------- -- 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 PadString( strIn, lenWanted ) strOut = strIn while string.len( strOut ) < lenWanted do strOut = strOut .. " " end return strOut end function PadNumString( numIn, lenWanted ) strOut = ""..numIn while string.len( strOut ) < lenWanted do strOut = strOut .. " " end return strOut end -- a function that parses a string of aa indices and returns them as a sorted array. function ParseAaString( str, AaArray ) -- fancy parsing here: Input format is: idx1,idx2-idx3,idx4,idx5,idx6-idx7,... for w in string.gmatch( str, "[^,]+" ) do if string.find( w, "-" ) ~= nil then local s, f = string.match( w, "(%d+)%-(%d+)" ) start = tonumber( s ) fin = tonumber( f ) for i=start,fin do local okToAdd = true for j=1,#AaArray do if AaArray[ j ] == i then okToAdd = false break end end if okToAdd then AaArray[ #AaArray+1] = i end end else local seg = tonumber( w ) local okToAdd = true for j=1,#AaArray do if AaArray[ j ] == seg then okToAdd = false break end end if okToAdd then AaArray[ #AaArray+1] = seg end end end if #AaArray > 0 then table.sort(AaArray) end end function TrimNum( val ) return val - val % 0.001 end ---------------------------------------------------------------- -- ACTION STATISTICS AND PREFERENCES ---------------------------------------------------------------- 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 ReportStats( ) -- scoreDelta turns out to be not very interesting to look at print("Action stats: " .. #algNameList ) for i = 1, #algNameList do local t = algNameList[ i ] if algStats[ t ] ~= nil and algStats[ t ].callCount > 0 then print( " " .. PadString(t, 20) .. ":" .. " 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 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 ---------------------------------------------------------------- -- 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 getSlotScore( qs ) save.Quicksave( QS_Swapslot ) save.Quickload( qs ) sc = getScore( ) save.Quickload( QS_Swapslot ) return sc end function SaveBest( forceBest, qs_best ) -- most callers will leave both as nil. if qs_best == nil then qs_best = QS_Best end if forceBest == nil then forceBest = false end local score = getScore( ) local CurrentBestScore = getSlotScore( qs_best ) -- use "score >=" because same-with-unfrozen/unbanded is better if forceBest or score >= CurrentBestScore then save.Quicksave( qs_best ) CurrentBestScore = score end if PuzzleHasContactMap then CurrentBestCmScore = getSlotScore( QS_CmBest ) cmScore = GetContactScore( ) if forceBest or (CONTACTMAP_TAKEDROPS and cmScore > CurrentBestCmScore) then save.Quicksave( QS_CmBest ) CurrentBestCmScore = score end if CONTACTMAP_TAKEDROPS and CurrentBestScore ~= CurrentBestCmScore then DebugPrint(" In Best("..qs_best.."): score="..CurrentBestScore ) DebugPrint(" In CmBest("..QS_CmBest.. "): score="..CurrentBestCmScore ) end end end function LoadBest( qs_best ) if qs_best == nil then qs_best = QS_Best end local CurrentBestScore = getSlotScore( qs_best ) if PuzzleHasContactMap and CONTACTMAP_TAKEDROPS then local CurrentBestCmScore = getSlotScore( QS_CmBest ) if CurrentBestCmScore >= CurrentBestScore - CONTACTMAP_MAX_DROP then save.Quickload( QS_CmBest ) else save.Quickload( qs_best ) end else save.Quickload( qs_best ) end end function GetBestScore( ) local CurrentBestScore = getSlotScore( qs_best ) if PuzzleHasContactMap and CONTACTMAP_ALLOWDROP then CurrentBestCmScore = getSlotScore( QS_CmBest ) if CurrentBestCmScore >= CurrentBestScore - CONTACTMAP_MAX_DROP then return CurrentBestCmScore else return CurrentBestScore end else return CurrentBestScore end end ---------------------------------------------------------------- -- RANDOM FUNCTIONS ---------------------------------------------------------------- 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 randomBool( probTrue ) if probTrue == nil or probTrue == 0.5 then local r = random(0, 1, false ) return r == 0 else local r = random() if r < probTrue then return true else return false 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( ) if seg == nil then break end 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 ---------------------------------------------------------------- -- SCORE-USING FUNCTIONS ---------------------------------------------------------------- function sortAllByScore(segs, scores) table.sort(segs, function(n1,n2) return scores[n1] < scores[n2] end) end 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 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 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 -- produces a list of all AAs "near" the ones in a given inputList function getSegsNearListed( inputList, radius) local retval = {} for i=1, segCt do for j=1, #inputList do if structure.GetDistance(i,j)<radius then retval[ #retval+1 ] = j break end end end return retval 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 function GetNearestPhobicsOutsideRange( seg, segExcludeStart, segExcludeEnd, nearPhobics, ctWanted ) local idxListInternal = {} local distList = {} for i=1, segCt do idxListInternal[ #idxListInternal + 1 ] = i end for i=1, segExcludeStart - 1 do if structure.IsHydrophobic( i ) then distList[ #distList + 1 ] = structure.GetDistance( seg, i ) else distList[ #distList + 1 ] = 1000 end end for i=segExcludeStart, segExcludeEnd do distList[ #distList + 1 ] = 10000 end for i=segExcludeEnd + 1, segCt do if structure.IsHydrophobic( i ) then distList[ #distList + 1 ] = structure.GetDistance( seg, i ) else distList[ #distList + 1 ] = 1000 end end sortAllByScore( idxListInternal, distList ) for i=1, ctWanted do if i > #idxListInternal then break end nearPhobics[ #nearPhobics + 1 ] = idxListInternal[ i ] end end function getSegsNotInList( list ) table.sort( list ) local result={ } if #list > 0 then for j = 1, list[1]-1 do result[ #result+1 ] = j end end for i = 1, #list-1 do for j = list[ i ]+1, list[ i+1 ]-1 do result[ #result+1 ]=j end end for j = list[ #list ]+1, segCt do result[ #result+1 ]=j end return result 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 -- real tip - 1 because maybe cysteine bridge 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 ) -- cysteine loses last atom if disulfide bridge if structure.GetAminoAcid( idx ) == "c" then return structure.GetAtomCount( idx ) else return GetAtomOfTip( structure.GetAminoAcid( idx )) end end function GetCysteineSulfurAtom( idx ) if structure.GetAminoAcid(idx) ~= "c" then return 0 end totalAtoms = structure.GetAtomCount(idx) if totalAtoms == 10 then -- there is a cysteine bond, so things shifted return 7 else return 6 end end function PickAtomNumberForBand( idx ) if not BAND_TO_SIDECHAINS then return CENTER_CARBON end local r = random( 1, 7 ) -- consider adjusting probability? if r <= 3 then -- do this for r = 1,2,3 return BETA_CARBON elseif r == 4 then -- do this rarely, because it's only rarely useful return GetTipAtomOfSeg( idx ) else -- do this for r = 5,6,7 return CENTER_CARBON end end function SetCysteineSegList( ) cysteineSegs = {} for i=1, segCt do lt = structure.GetAminoAcid( i ) if lt == "c" or lt == "C" then cysteineSegs[#cysteineSegs + 1] = i end end end function GuessCysteinePairs( ) ctPairsWanted = math.floor( #cysteineSegs / 2 ) cysteinePairs = {} localCystList = {} for i=1, #cysteineSegs do localCystList[i] = cysteineSegs[i] end for ii = 1, ctPairsWanted do minDist = 1000000.0 bestI = 0 bestJ = 0 for i = 1, #localCystList - 1 do cyst1 = localCystList[i] for j=i+1, #localCystList do cyst2 = localCystList[j] -- don't try to match up "adjacent" cysteines. if cyst2 - cyst1 > 3 then distBetween = structure.GetDistance( cyst1, cyst2 ) if distBetween < minDist then bestI = i bestJ = j minDist = distBetween end end end end cysteinePairs[#cysteinePairs+1] = {localCystList[bestI], localCystList[bestJ]} table.remove( localCystList, bestJ ) table.remove( localCystList, bestI ) end return true end function DoesCysteineBondExist( idx1, idx2) -- NOTE: I have problems if two cysteine bonds exist involving the -- two given segments but the bonds are not actually between -- my two segments (eg a<->c and b<->d exist, I want a<->b). -- -- My attempts to use distance are kludgy but I don't have -- better (can't ask for distance between atoms), so I will -- leave the problem in situ. -- structure.GetAtomCount could also be checked (10 vs 11) -- but that doesn't help the core problem here. -- dist <= 6.996 seems to correspond to success. dist = structure.GetDistance(idx1, idx2 ) dScore1 = current.GetSegmentEnergySubscore( idx1, "disulfides" ) dScore2 = current.GetSegmentEnergySubscore( idx2, "disulfides" ) bondExists = (dist < 7.5 and dScore1 ~= 0.0 and dScore2 ~= 0.0) --if bondExists then -- print( "CBond "..idx1.."-"..idx2.. -- " dist="..TrimNum(dist).. -- " sc1="..TrimNum(dScore1).. -- " sc2="..TrimNum(dScore2)) --end return bondExists end function CheckCysteineSanity() for i = 1, #cysteinePairs do pair = cysteinePairs[i] dist = structure.GetDistance( pair[1], pair[2] ) if dist > MAX_RI_CYSTEINE_SEPARATION then print( "Rejected action - cysteine "..pair[1].." far from "..pair[2]) return false end --print("distance "..pair[1].." to "..pair[2].." is "..dist) end return true 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 ---------------------------------------------------------------- -- 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( ) --if IsSlowFilterDisabled then -- print("slow filters are now ON") --end behavior.SetSlowFiltersDisabled( IsSlowFilterDisabled ) end function CheckForSlowFilters( ) TurnOffSlowFilters( ) local retval = not current.AreConditionsMet() ResetSlowFilterState( ) return retval end ---------------------------------------------------------------- -- LOCKED/FROZEN FUNCTIONS ---------------------------------------------------------------- function GenerateNonLockedSegList( ) for i=1, segCt do if structure.IsLocked( i ) then PuzzleHasLockedSegs = true else NonLockedSegList[ #NonLockedSegList + 1] = i end end end function IsUnlockedSeg( seg ) return not structure.IsLocked( seg ) end function FindAllMovableSegs( ) 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 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 SelectNonFrozens( idxList ) local segs = { } local segsToCheck = idxList if segsToCheck == nil then segsToCheck = { } for i = 1, segCt do segsToCheck[ #segsToCheck + 1] = i end end selection.DeselectAll( ) for i = 1, #segsToCheck do b,s = freeze.IsFrozen( segsToCheck[ i ] ) if not s then segs[ #segs + 1 ] = segsToCheck[ i ] end end for i = 1, #segs do selection.Select( segs[ i ] ) 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 isExcellentHelixSeg( seg ) aa = structure.GetAminoAcid( seg ) return aa == 'a' or aa == 'e' or aa == 'm' or aa == 'k' end function isGoodHelixSeg( seg ) aa = structure.GetAminoAcid( seg ) return isExcellentHelixSeg(seg) or aa == 'f' or aa == 'j' or aa == 'l' or aa == 'q' or aa == 'v' or aa == 'w' end function isOkHelixSeg( seg ) aa = structure.GetAminoAcid( seg ) return isGoodHelixSeg(seg) or aa == 'h' or aa == 'r' or aa == 's' or aa == 't' end function isHelixBreaker( seg ) return structure.GetAminoAcid( seg ) == 'p' end function isBadNonBreakerHelixSeg( seg ) aa = structure.GetAminoAcid( seg ) return aa == 'g' or aa == 'd' or aa == 'n' end function LocateHelices( ) inside = false helixStart = 0 allHelices = {} for i=1, segCt do if IsUnlockedSeg( i ) then lastUnlocked = i end if ( IsUnlockedSeg( i ) and structure.GetSecondaryStructure(i) == "H" ) then if ( inside == false ) then inside = true helixStart = i end elseif inside == true then inside = false allHelices[#allHelices+1] = { sIdx = helixStart, eIdx = i-1 } end end -- deal with "last seg is helix" if ( inside == true ) then allHelices[#allHelices+1] = {sIdx = helixStart, eIdx = lastUnlocked } end end function LocateSheets( ) inside = false sheetStart = 0 allSheets = {} for i=1, segCt do if IsUnlockedSeg( i ) then lastUnlocked = i end if ( IsUnlockedSeg( i ) and structure.GetSecondaryStructure(i) == "E" ) then if ( inside == false ) then inside = true sheetStart = i end elseif inside == true then inside = false allSheets[#allSheets + 1] = {sIdx = sheetStart, eIdx = i - 1} end -- if/else 'E' end -- for (segCt) -- deal with "last seg is sheet" if ( inside == true ) then allSheets[#allSheets + 1] = {sIdx = sheetStart, eIdx = lastUnlocked} end end function LocateLoops( ) inside = false loopStart = 0 allLoops = {} for i=1, segCt do if IsUnlockedSeg( i ) then lastUnlocked = i end if ( IsUnlockedSeg( i ) and structure.GetSecondaryStructure(i) == "L" ) then if ( inside == false ) then inside = true loopStart = i end elseif inside == true then inside = false allLoops[ #allLoops + 1 ] = {sIdx = loopStart, eIdx = i - 1} end -- if/else 'L' end -- for (segCt) -- deal with "last seg is loop" if ( inside == true ) then allLoops[ #allLoops + 1 ] = {sIdx = loopStart, eIdx = lastUnlocked} end end function FindFirstProbableHelixInSeglist( sIdx, eIdx ) startHelix = 0 endHelix = 0 if eIdx - sIdx < 6 then -- not worth looking for one here return 0, 0 end for i = sIdx, eIdx - 5 do startHelix = 0 endHelix = 0 if isGoodHelixSeg( i ) then --print("Possible start at idx="..i.." aa="..structure.GetAminoAcid(i) ) startHelix = i for j=i+1, eIdx do endHelix = j if isOkHelixSeg( j ) then -- reset mediocre count because we don't mind -- isolated mediocres, just don't want 2 in a row --print("Saw good "..structure.GetAminoAcid(j).." at "..j) elseif isHelixBreaker( j ) then endHelix = j-1 --print("Saw proline at "..j) break elseif isBadNonBreakerHelixSeg( j ) then endHelix = j-1 --print("Saw bad "..structure.GetAminoAcid(j).." at "..j) break else --print( "Saw mediocre "..structure.GetAminoAcid(j).." at "..j ) nextOk = true nextNextOk = true if j < segCt - 1 then nextNextOk = isOkHelixSeg( j + 2 ) end if j < segCt then nextOk = isOkHelixSeg( j + 1 ) end if not (nextOk and nextNextOk) then break end end end if endHelix - startHelix > 4 then return startHelix, endHelix end end end return 0, 0 end function LocateMissingHelices() possibleHelices = {} for i = 1, #allLoops do bounds = allLoops[i] --print( "looking at loop: "..bounds.sIdx.."-"..bounds.eIdx ) if bounds.eIdx - bounds.sIdx >= 7 then currStart = bounds.sIdx while currStart < bounds.eIdx - 4 do startHelix, endHelix = FindFirstProbableHelixInSeglist( currStart, bounds.eIdx ) if startHelix == 0 then break -- no more helices to find in this stretch of loopiness else --print("Found possible helix at s,e="..startHelix..","..endHelix) possibleHelices[#possibleHelices + 1] = {sIdx = startHelix, eIdx = endHelix} currStart = endHelix + 2 end end end end end function isPossibleHelix( idx ) for i = 1, #possibleHelices do bound = possibleHelices[ i ] if bound.sIdx <= idx and bound.eIdx >= idx then s = bound.sIdx e = bound.eIdx return true end end return false 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 if typeIdx == 'l' then -- if we think there's a helix hidden here, let's try to offer its boundaries for i = 1, #possibleHelices do bound = possibleHelices[ i ] if bound.sIdx <= idx and bound.eIdx >= idx then s = bound.sIdx e = bound.eIdx return s, e end end 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 if eIdx - sIdx <= 1 then -- last try: unless we're at an end, just give ourselves one before and after if sIdx > 1 then sIdx = sIdx - 1 end if eIdx < segCt then eIdx = eIdx + 1 end end if eIdx - sIdx <= 1 then return idx,idx end -- failed, something is very wrong end -- Check: if loop and more than 30-long, then only take part of it if regionType == 'L' and eIdx - sIdx > 30 and not isPossibleHelix( sIdx ) 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 function GetCI( ) return behavior.GetClashImportance( ) 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 CutEverywhereInRange( min, max ) if min == nil or min > segCt then min = 1 end if max == nil or max < min then max = segCt end for i=min, max do structure.InsertCut( i ) end end function DeleteAllCuts( min, max ) if min == nil or min > segCt then min = 1 end if max == nil or max < min then max = segCt end for i=min, max do structure.DeleteCut( i ) end end function AddRandomCuts( cutList, maxCuts ) for i=1, maxCuts do local seg = randomMovableSeg( ) 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 ------------------------------------------------------------------------------ -- ---- 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 = SHAKESIDECHAIN_ITERS end if MUTATE_NOT_SHAKE then SelectNonFrozens( idxList ) if #NoMutateList > 0 then -- deselect all no-mutate aas for i = 1, #NoMutateList do selection.Deselect( NoMutateList[ i ] ) end end structure.MutateSidechainsSelected( iters ) if #ShakeAfterMutateList > 0 then selection.DeselectAll( ) for i = 1, #ShakeAfterMutateList do local b,s = freeze.IsFrozen( ShakeAfterMutateList[ i ] ) if not s then selection.Select( ShakeAfterMutateList[ i ] ) end end structure.ShakeSidechainsSelected( iters ) end else SelectNonFrozens( idxList ) structure.ShakeSidechainsSelected( iters ) end selection.DeselectAll( ) TotalShakeTime = TotalShakeTime + ( os.time( ) - startTime ) end function LongWiggle( ) if USEITERATIVELONGWIGGLE then IterativeWiggleAll( true, true ) else structure.WiggleAll( LONGWIGGLE_ITERS ) end end -- no recentbest here because sometimes we'd call while decreasing score function WiggleRange( 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( QUICKWIGGLE_ITERS, true, true ) else structure.LocalWiggleSelected( QUICKWIGGLE_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( doBackbone, doSidechain ) local lastScore = getQuickScore( ) local gain = QUICKWIGGLE_GAIN recentbest.Save( ) while gain >= QUICKWIGGLE_GAIN do structure.WiggleAll( QUICKWIGGLE_ITERS, doBackbone, doSidechain ) -- do's ok for nil recentbest.Restore( ) gain = getQuickScore( ) - lastScore lastScore = getQuickScore( ) end end --------------------------------------------- -- SIMPLE STABILIZER WIGGLES --------------------------------------------- -- currently unused function IterativeWiggleList( idxList, minGain, doBackbone, doSidechain ) print( "IterativeWiggleList" ) selection.DeselectAll( ) for i=1, #idxList do selection.Select( i ) end local lastScore = getQuickScore( ) local gain = minGain while gain >= minGain do structure.WiggleSelected( QUICKWIGGLE_GAIN, doBackbone, doSidechain ) -- nil is allowed for do* gain = getQuickScore( ) - lastScore lastScore = getQuickScore( ) end selection.DeselectAll( ) end function WiggleShakeWiggleList( idxList ) print( "WiggleShakeWiggleList" ) recentbest.Save( ) sc = getQuickScore( ) selection.DeselectAll( ) for i=1, #idxList do selection.Select( i ) end structure.WiggleSelected( LONGWIGGLE_ITERS ) selection.DeselectAll( ) recentbest.Restore( ) ShakeOrMutate( SHAKESIDECHAIN_ITERS, idxList ) recentbest.Restore( ) if math.abs( getQuickScore( ) - sc) > 0.1 then -- otherwise, why bother? LongWiggle( ) end recentbest.Restore( ) end function qStabWiggle( doExtraWork ) print( "qStabWiggle" ) if doExtraWork == nil then doExtraWork = false end SetCI( 1.0 ) recentbest.Save( ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) recentbest.Restore( ) if doExtraWork then SetCI( 0.4 ) LongWiggle( ) recentbest.Restore( ) SetCI( 1.0) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) recentbest.Restore( ) end SetCI( 1.0) LongWiggle( ) recentbest.Restore( ) end -- the only user for now is the idealizer -- tends to produce transients function WiggleZlbNicely( strength, skipLower, skipUpper ) print( "WiggleZlbNicely" ) if skipLower == nil then skipLower = MaxUnlockedSeg+1 end if skipUpper == nil then skipUpper = MinUnlockedSeg-1 end recentbest.Save( ) ZLB(strength, skipLower, skipUpper) LongWiggle( ) recentbest.Restore( ) DelBands( ) recentbest.Save( ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) recentbest.Restore( ) end function SimpleRegionWiggle( idxLo, idxHi, pickGlobalWiggle, wiggleExpansion ) if pickGlobalWiggle then print( "SimpleRegionWiggle-global") else print( "SimpleRegionWiggle-local") end 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( startIdx, endIdx, false, pickGlobalWiggle ) end function VerySimpleShakeWiggle( ) print( "VerySimpleShakeWiggle" ) recentbest.Save( ) ExactSetCI( 0.05 ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) SetCI( 1.0 ) LongWiggle( ) recentbest.Restore( ) end function SimpleWiggleShake( ) print( "SimpleWiggleShake" ) recentbest.Save( ) local oldRbScore = getRBScore( ) SetCI( COREPULLING_CI ) if randomBool( 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 end --------------------------------------------------------- --BAND-CHANGING WRAPUP WIGGLERS --------------------------------------------------------- function CleanShakeQStab() DelBands( ) ResetFrozenness( ) SetCI( 1.0 ) -- shake first to try to make modification acceptable ShakeOrMutate( SHAKESIDECHAIN_ITERS ) -- wiggle to complete the action qStabWiggle( randomBool( PROB_QSTAB_EXTRA )) end function ShakeWiggleCleanShakeWiggle( ) print( "ShakeWiggleCleanShakeWiggle" ) recentbest.Save( ) local oldRbScore = getRBScore( ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) if randomBool( 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( SHAKESIDECHAIN_ITERS ) LongWiggle( ) recentbest.Restore( ) end function BandedWiggleUntilEnoughChange( scoreThreshold, pullingCI ) print( "BandedWiggleUntilEnoughChange" ) 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 randomBool( PROB_LOCALWIGGLE ) then structure.LocalWiggleAll( QUICKWIGGLE_ITERS ) else structure.WiggleAll( QUICKWIGGLE_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 SimpleWiggleShakeWrapper( minChangeWanted, pullingCI ) if minChangeWanted == nil then minChangeWanted = BANDEDWIGGLE_TARGET end if pullingCI == nil then pullingCI = COREPULLING_CI end local scoreThreshold = math.min( minChangeWanted, getQuickScore( ) / 500.0) recentbest.Save( ) local oldRbScore = getRBScore( ) BandedWiggleUntilEnoughChange( scoreThreshold, pullingCI ) local newRbScore = getRBScore( ) if newRbScore > oldRbScore then recentbest.Restore( ) end DelBands( ) ResetFrozenness( ) recentbest.Save( ) qStabWiggle( randomBool( PROB_QSTAB_EXTRA )) recentbest.Restore( ) end -- unused (BandedWiggleUntilEnoughChange is preferred) function BandedWiggleUntilTargetMet( 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.LocalWiggleAll( QUICKWIGGLE_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( startIdx, endIdx ) recentbest.Save( ) local oldRbScore = getRBScore( ) SetCI( COREPULLING_CI ) SimpleRegionWiggle( startIdx, endIdx, false, 2 + random( 4 )) local newRbScore = getRBScore( ) if newRbScore > oldRbScore then recentbest.Restore( ) end DelBands( ) -- if we put in bands, they go now ResetFrozenness( ) -- if we froze anything, clean that up now SetCI( 1.0 ) if randomBool( ) then qStabWiggle( randomBool( PROB_QSTAB_EXTRA )) else recentbest.Save( ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) recentbest.Restore( ) SimpleRegionWiggle( startIdx, endIdx, true, random( 4, 8 )) recentbest.Restore( ) LongWiggle( ) end recentbest.Restore( ) end function WiggleShakeCleanQStab( ) SimpleWiggleShake( ) SetCI( 1.0 ) DelBands( ) -- if we put in bands, they go now ResetFrozenness( ) -- if we froze anything, clean that up now recentbest.Save( ) qStabWiggle( randomBool( PROB_QSTAB_EXTRA )) recentbest.Restore( ) end function PickBeforeAndAfterWiggler( doVariableStrength ) if USE_LOWCI_WIGGLES then ShakeWiggleCleanShakeWiggle( ) elseif doVariableStrength then SimpleWiggleShakeWrapper( BANDEDWIGGLE_TARGET, COREPULLING_CI ) else WiggleShakeCleanQStab( ) 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 randomBool( 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: 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 -------------------------------------------------- -- FUSES AND OTHER SLOW 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(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( 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 ) SetCI( ci ) structure.WiggleAll( QUICKWIGGLE_ITERS ) 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 ) -- aka Fuze1 SetCI( ci1 ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) SetCI( ci2 ) LongWiggle( ) end function WiggleWiggleWiggleFuse( ci1, ci2 ) -- aka Fuze2 SetCI( ci1 ) LongWiggle( ) SetCI( 1.00 ) LongWiggle( ) SetCI( ci2 ) LongWiggle( ) end function WiggleShakeWiggleFuse( ) -- aka FuzeEnd SetCI( 1.00 ) LongWiggle( ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) LongWiggle( ) end ---------------------------------------------------------------------- -- ---- BANDER FUNCTIONS -- ---------------------------------------------------------------------- function EnableAndDisableCysteineBands( ) for i=1, CysteineBandPairCount do bIdx = InitialBandCount + COUNT_CYSTEINE_BANDS_USED*(i-1) if bIdx > band.GetCount() then print("bIdx too large - will not enable or disable") else pair = cysteinePairs[i] bondExists = DoesCysteineBondExist( pair[1], pair[2] ) for j=1, COUNT_CYSTEINE_BANDS_USED do if bondExists then band.Disable( bIdx + j ) else band.Enable( bIdx + j ) end end end end end function DelBands( alsoCysteine ) if alsoCysteine == nil then alsoCysteine = false end if alsoCysteine and not PRESERVE_USER_BANDS then print("Wiping all bands") band.DeleteAll( ) else local allBandCount = band.GetCount( ) firstBandToDel = 1 + InitialBandCount if not alsoCysteine then firstBandToDel = firstBandToDel + COUNT_CYSTEINE_BANDS_USED*CysteineBandPairCount end for i=allBandCount, firstBandToDel, -1 do -- this *must* go in reverse order band.Delete( i ) end end end function SetMyBandStrengths( strength ) allBandCount = band.GetCount( ) firstActionBand = InitialBandCount + COUNT_CYSTEINE_BANDS_USED*CysteineBandPairCount + 1 for i=firstActionBand, allBandCount 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 -- SNH: don't accidentally change user-supplied bands return true end 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 0 end if bIdx <= InitialBandCount+COUNT_CYSTEINE_BANDS_USED*CysteineBandPairCount then -- don't change user-supplied bands -- don't expect to hit our cysteine ones, but just to be safe... return 0 end if goalLength ~= nil then band.SetGoalLength( bIdx, goalLength ) end if strength ~= nil and strength ~= 0.0 then band.SetStrength( bIdx, strength ) end return bIdx end -- fool with this later, does not work function BandInSpaceFromAtomWithParameters( seg, segFini, segInit, rho, theta, phi, strength, goalLength, atom) if not IsUnlockedSeg( seg ) then return false end local atomXAxis = 1 -- atoms 1,2,3 are on the backbone. local atomYAxis = 2 if atom == 1 then atomXAxis, atomYAxis = 2,3 end if atom == 2 then atomXAxis, atomYAxis = 1,3 end local bIdx = band.Add( seg, segFini, segInit, rho, theta, phi, atom, atomXAxis, atomYAxis ) if bIdx ~= band.GetCount( ) then return 0 end if bIdx <= InitialBandCount+COUNT_CYSTEINE_BANDS_USED*CysteineBandPairCount then -- don't change user-supplied bands -- don't expect to hit our cysteine ones, but just to be safe... return 0 end if goalLength ~= nil then band.SetGoalLength( bIdx, goalLength ) end if strength ~= nil and strength ~= 0.0 then band.SetStrength( bIdx, strength ) end return bIdx end function ZLB( strength, lower, upper, bandGivenRange ) if lower == nil then lower = MaxUnlockedSeg + 1 end if upper == nil then upper = MinUnlockedSeg - 1 end if bandGivenRange == nil then bandGivenRange = false end -- default is exclude a range if bandGivenRange then for i=lower, upper do if i == 1 then BandInSpaceWithParameters( i, i+1, i+2, 0.01, 0.0, 0.0, strength, 0.01) elseif i == segCt then BandInSpaceWithParameters( i, i-1, i-2, 0.01, 0.0, 0.0, strength, 0.01) else BandInSpaceWithParameters( i, i+1, i-1, 0.01, 0.0, 0.0, strength, 0.01) end end else for i=MinUnlockedSeg+1, MaxUnlockedSeg-1 do if i < lower or i > upper then BandInSpaceWithParameters( i, i+1, i-1, 0.01, 0.0, 0.0, strength, 0.01) end end if lower > 1 then BandInSpaceWithParameters( 1, 2, 3, 0.01, 0.0, 0.0, strength, 0.01) end if upper < segCt then BandInSpaceWithParameters( segCt, segCt-1, segCt-2, 0.01, 0.0, 0.0, strength, 0.01) end 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 PutRandomCompBands() if not BAND_ADD_RANDOMS then return end if randomBool( ) then 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 ---------------------------------------------------------------------- -- MID-SCALE BANDERS ---------------------------------------------------------------------- function PutGlycineHingeBands( glycineIdx, strength ) 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 function PutGeneralHingeBands( startHinge, endHinge, strength ) 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 -- should only happen if startHinge or endHinge is too close to an end return changeFailed end for i=1, maxLen do if BandBetweenSegsWithParameters( math.max(1, startHinge - i), math.min(segCt, endHinge + i), strength ) then changeSucceeded = true end end if not changeSucceeded then ResetFrozenness( ) end return changeSucceeded end function PutCysteineBands( idx1, idx2 ) if idx1 < 1 or idx1 > segCt or idx2 < 1 or idx2 > segCt or idx1 == idx2 then return false end if structure.GetAminoAcid( idx1 ) ~= 'c' or structure.GetAminoAcid( idx2 ) ~= 'c' then return false end at1 = GetTipAtomOfSeg( idx1 ) at2 = GetTipAtomOfSeg( idx2 ) -- fancy is to put more than one band, going after the sulfur atom: -- Put bands here: -- atS1 -> atS2 dist=d1=DISTANCE_CYSTEINE_BOND -- atS1 -> atS2-1 dist=d2=math.sqrt(d1*d1+1.8*1.8) -- atS1-1 -> atS2 dist=d2 -- atS1-1 -> atS2-1 dist=d3=math.sqrt(d2*d2+1.8*1.8) atS1 = GetCysteineSulfurAtom( idx1 ) atS2 = GetCysteineSulfurAtom( idx2 ) strength = CYSTEINE_BAND_STRENGTH d1 = DISTANCE_CYSTEINE_BOND if COUNT_CYSTEINE_BANDS_USED == 1 then -- my old way of doing this return BandBetweenSegsWithParameters( idx1, idx2, strength, d1, at1, at2 ) else d2 = math.sqrt(d1*d1 + SCALE_CYSTEINE_OFF_1*SCALE_CYSTEINE_OFF_1) d3 = math.sqrt(d2*d2 + SCALE_CYSTEINE_OFF_1*SCALE_CYSTEINE_OFF_1) BandBetweenSegsWithParameters( idx1, idx2, strength, d1, atS1, atS2 ) BandBetweenSegsWithParameters( idx1, idx2, strength, d2, atS1, atS2-1 ) if COUNT_CYSTEINE_BANDS_USED >= 3 then BandBetweenSegsWithParameters( idx1, idx2, strength, d2, atS1-1, atS2 ) end if COUNT_CYSTEINE_BANDS_USED >= 4 then BandBetweenSegsWithParameters( idx1, idx2, strength, d3, atS1-1, atS2-1 ) end end return true -- just assume it worked! end function PutCysteinePairBands( ) changeSucceeded = false CysteineBandPairCount = 0 for i=1, #cysteinePairs do pair = cysteinePairs[i] chSuc = PutCysteineBands( pair[1], pair[2] ) if chSuc then -- will fail if user already has one of these in place CysteineBandPairCount = CysteineBandPairCount + 1 end changeSucceeded = changeSucceeded or chSuc end return changeSucceeded end function PutPushPullBands( badSeg, bandsWanted, center, strength, wantPushNotPull ) 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 if #okSegList == 0 then print( "No suitable segs for idx="..badSeg.." with center="..center) return false 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" 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 ) 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 ) 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 else 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 ) 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 ) 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 ) changeSucceeded = false init = math.max( 1, startSeg - 1 ) fini = math.min( segCt, endSeg + 1 ) if doForward then for i = init + 1, fini - 1 do if BandInSpaceWithParameters( i, fini, init, length, math.pi/2, 0.0, strength ) then changeSucceeded = true end end else for i = fini - 1, init + 1, -1 do if BandInSpaceWithParameters( i, fini, init, length, math.pi/2, 0.0, strength ) then changeSucceeded = true end end end return changeSucceeded end function PutMoverBands( startSeg, endSeg, length, strength ) 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 ) 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 ) changeSucceeded = false for i=startSeg, endSeg-2 do changeSucceeded = BandBetweenSegsWithParameters( i, i+2, DISTANCE_INSHEET_N_TO_NPLUS2, 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 function PutTailDetacherBands( fromStart, length ) if fromStart then s = 1 e = 1 + length rel = 2 + length else s = segCt - length - 1 e = segCt rel = segCt - length - 2 end if rel < 1 or rel > segCt then return 0 end goalLen = 0 phi = random( 0.5 * math.pi ) theta = math.pi/2.0 + random( -0.5 * math.pi, 0.5 * math.pi ) if fromStart then for i=e,s+1,-1 do goalLen = goalLen + 3.5 BandInSpaceWithParameters( i, e+1, e+2, length, theta, phi, 1.0, goalLen ) end else goalLen = goalLen + 3.5 for i=s,e do BandInSpaceWithParameters( i, s-1, s-2, length, theta, phi, 1.0, goalLen ) end end return true end function PutTailAttacherBands( fromStart, length ) if fromStart then s = 1 e = 1 + length else s = segCt - length - 1 e = segCt end -- Idea is to find hydrophobics between s and e and attach bands -- from some of them to a "randomly" chosen nearby hydrophobic -- not between s and e. local hookAas = { } for i=s, e do if structure.IsHydrophobic( i ) then hookAas[ #hookAas + 1] = i end end if #hookAas == 0 then return false end -- if we have no hydrophobics then give up for now randomizeIndexList( hookAas) local ctToHook = random(1, #hookAas ) for i=1, ctToHook do local nearPhobics = {} toHook = hookAas[ i ] GetNearestPhobicsOutsideRange( toHook, s, e, nearPhobics, 5 ) which = random( 1, #nearPhobics ) BandBetweenSegsWithParameters( toHook, nearPhobics[which], 1.0, 2.0, BETA_CARBON, BETA_CARBON ) end return true end ---------------------------------------------------------------------- -- LARGE-SCALE BANDERS ---------------------------------------------------------------------- function PutSheetSewerBandsAllOver( maxDist, strength ) 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 ) if doSidechains == nil then doSidechains = false end if pullPhilics == nil then pullPhilics = false end 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 ) 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 ) local bandsAdded = 0 local failCount = 0 repeat local s = randomMovableSeg( ) 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, omitLongBands ) 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 not omitLongBands or goalLength < MAX_ALLOWED_NONCONTACT then 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 end return changeSucceeded end function PutContactMapBands( ) 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 randomBool( ) local wantDither = randomBool( ) local doVariableStrength = false return PutAllContactMapBands( threshold, madeStrength, madeLenScale, unmadeStrength, unmadeLenScale, doSidechains, wantDither, true ) end function PutSomeContactMapBands( heatThreshold, strength, ctBands, doSidechains, omitLongBands ) 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 if not omitLongBands or structure.GetDistance( i, j ) < MAX_ALLOWED_NONCONTACT then hotList[ #hotList + 1] = { i, j, heat } end 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, omitLongBands ) 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 if not omitLongBands or goalLength < MAX_ALLOWED_NONCONTACT then 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 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 if not omitLongBands or structure.GetDistance( i, idx ) < MAX_ALLOWED_NONCONTACT then hotList[ #hotList + 1] = i end 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, omitLongBands ) 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 not omitLongBands or goalLength < MAX_ALLOWED_NONCONTACT then 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 end return changeSucceeded end function PutPushPullContactMapBands( badSeg, bandsWanted, center, strength, heatThreshold, wantPushNotPull ) 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 ) 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 IdealizeSelected( ) recentbest.Save() band.DisableAll( ) structure.IdealizeSelected( ) band.EnableAll( ) EnableAndDisableCysteineBands( ) if not CheckCysteineSanity() then print("Rejected idealize due to cysteine insanity") recentbest.Restore() return false end return true end function IdealizeInRange( startSeg, endSeg, useCuts ) 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 changeSucceeded = IdealizeSelected( ) selection.DeselectAll( ) if changeSucceeded then if useCuts then -- get some wiggling happening WiggleZlbNicely( 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 recentbest.Save( ) SimpleRegionWiggle( startSeg, endSeg, randomBool() ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) recentbest.Restore( ) end end if changeSucceeded and not changeFailed then DelBands( ) return true end tries = tries + 1 delta = delta + 0.25 until tries >= IDEALIZE_MAXTRIES DelBands( ) return false end function IdealizeInRange2( startSeg, endSeg ) local idxList = { } save.Quicksave( QS_ShortUseTemp1 ) if startSeg > 2 then structure.InsertCut( startSeg - 1 ) end if endSeg < segCt then structure.InsertCut( endSeg ) end selection.DeselectAll( ) selection.SelectRange( startSeg, endSeg ) changeSucceeded = IdealizeSelected( ) if changeSucceeded then if startSeg > 2 then if not DeleteCutAtIndex( startSeg - 1 ) then changeFailed = true end end if endSeg < segCt then if not DeleteCutAtIndex( endSeg ) then changeFailed = true end end end if changeFailed or not changeSucceeded then save.Quickload( QS_ShortUseTemp1 ) return false -- just give up end selection.DeselectAll( ) recentbest.Save( ) getSegsWithinRangeOfTube( idxList, startSeg, endSeg, IDEALIZE_TUBE_BIG_RADIUS ) for i=1, #idxList do selection.Select( idxList[i] ) end structure.WiggleSelected( LONGWIGGLE_ITERS ) selection.DeselectAll( ) recentbest.Restore( ) return true end function lowCiIdealizer() if IdealizeMin == nil then IdealizeMin = 1 end if IdealizeMax == nil then IdealizeMax = segCt end ExactSetCI( 0.05 ) ZLB( IDEALIZEALL_ZLB_STRENGTH ) selection.SelectRange( IdealizeMin, IdealizeMax ) changeSucceeded = IdealizeSelected( ) if changeSucceeded then structure.WiggleAll( 30 ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) structure.WiggleAll( 30 ) selection.DeselectAll( ) end DelBands( ) print( "After Initial Idealizer = "..TrimNum( getScore( ))) return changeSucceeded end ---------------------------------------------------------------------- -- REBUILDERS ---------------------------------------------------------------------- function RebuildSelected( iters ) recentbest.Save() band.DisableAll( ) structure.RebuildSelected( iters ) band.EnableAll( ) EnableAndDisableCysteineBands( ) if not CheckCysteineSanity() then print("Rejected rebuild due to cysteine insanity") recentbest.Restore() return false end return getQuickScore( ) ~= initscore end function GetRebuildRange( idx, maxDist ) if maxDist == nil then maxDist = math.floor( REBUILD_MAX_SEGLEN / 2 ) end local startIdx = math.max( 1, idx - random( maxDist )) local endIdx = math.min( segCt, idx + random( maxDist )) 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 startIdx, endIdx end function RebuildInRange( idxStart, idxEnd, maxTries, maxFall ) if maxFall == nil then maxFall = REBUILD_MAX_MEDIUM_SCOREDROP end if maxTries == nil then maxTries = REBUILD_TRYCOUNT end wantHelix = isPossibleHelix( idxStart ) changeSucceeded = false local iters = QUICKWIGGLE_ITERS local oldScoreMat = {} local locss = {} local changedSS = false local startScore = getQuickScore( ) ConstructScoreMatrix( oldScoreMat ) -- probably PickRebuild should decide whether to do secondary structure stuff for i=idxStart, idxEnd do locss[i] = structure.GetSecondaryStructure( i ) end for i=idxStart, idxEnd do if wantHelix then -- we think there's a helix, so tell rebuild to go with it changedSS = true structure.SetSecondaryStructure( i, 'H' ) elseif structure.GetSecondaryStructure( i ) == 'E' then -- sheet isn't worth telling rebuild about changedSS = 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( idxStart, idxEnd ) -- this param of "i" is weird: shouldn't this be iters? changeSucceeded = RebuildSelected( i ) selection.DeselectAll( ) if changeSucceeded then -- give the whole thing a little bit of shake-and-wiggle-time (deliberately not complete) structure.WiggleSelected( iters ) getSegsWithinRangeOfTube( idxListInner, idxStart, idxEnd, 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 end end end save.Quickload( QS_ShortUseTemp2 ) if changedSS then for i=idxStart, idxEnd do structure.SetSecondaryStructure( i, locss[ i ] ) end end selection.DeselectAll( ) return changeSucceeded end -- this does not have a quake, but it is the rebuild action seen in quake variants function RebuildSection_FromQuaker( idxStart, idxEnd, maxTries, maxFall ) local changeSucceeded = false local initScore = getQuickScore( ) local tries = 0 repeat selection.DeselectAll( ) selection.SelectRange( idxStart, idxEnd ) tries = tries + 1 chSuc = RebuildSelected( tries ) -- again, steadily increasing iterations? changeSucceeded = changeSucceeded or chSuc newScore = getQuickScore( ) until newScore ~= initScore or tries > maxTries if changeSucceeded then recentbest.Save( ) -- N.B. This is what recentbest was made for! RebuildSelected( maxTries - 1 ) recentbest.Restore( ) WiggleAndShakeForRebuilder( initScore, maxFall ) -- quaking rebuild put a perform-quake-if-score-not-better-but-not-too-bad here end return changeSucceeded end function RebuildSimple( idxStart, idxEnd, addIdealize ) if idxStart < 1 then idxStart = 1 end if idxEnd > segCt then idxEnd = segCt end if addIdealize == nil then addIdealize = REBUILD_ADDIDEALIZE end local initscore = getQuickScore( ) local idxList = { idxStart } len = idxEnd - idxStart - 1 for i=1, idxEnd - 1 do if idxStart + i <= segCt then idxList[ #idxList + 1 ] = idxStart + i else break end end selection.DeselectAll( ) selection.SelectRange( idxStart, idxEnd ) changeSucceeded = RebuildSelected( 1 ) if changeSucceeded then print( "RS-Wiggler") recentbest.Save( ) ShakeOrMutate( SHAKESIDECHAIN_ITERS, idxList ) recentbest.Restore( ) if addIdealize then chSuc = IdealizeSelected( ) if chSuc then recentbest.Restore( ) ShakeOrMutate( SHAKESIDECHAIN_ITERS, idxList ) recentbest.Restore( ) structure.WiggleAll( 25 ) recentbest.Restore( ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) end recentbest.Restore( ) end LongWiggle( ) recentbest.Restore( ) end return changeSucceeded end function RebuildSimple2( idxStart, idxEnd ) local initscore = getQuickScore( ) selection.DeselectAll( ) selection.SelectRange( idxStart, idxEnd) changeSucceeded = RebuildSelected( 1 ) if changeSucceeded then print( "RS2-Wiggler") 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 ) LongWiggle( ) recentbest.Restore( ) end return changeSucceeded end function RebuildSimple3( idxStart, idxEnd, addIdealize ) if addIdealize == nil then addIdealize = REBUILD_ADDIDEALIZE end local initscore = getQuickScore( ) recentbest.Save() selection.DeselectAll( ) selection.SelectRange( idxStart, idxEnd ) changeSucceeded = RebuildSelected( 4 ) if changeSucceeded then print( "RS3-Wiggler") recentbest.Save( ) if addIdealize then chSuc = IdealizeSelected( ) if not chSuc then recentbest.Restore() end end VerySimpleShakeWiggle( ) recentbest.Restore( ) end return changeSucceeded end function RebuildBitspawn( idxStart, idxEnd, useLowCIRules ) local initscore = getQuickScore( ) selection.DeselectAll( ) selection.SelectRange( idxStart, idxEnd ) changeSucceeded = RebuildSelected( 4 ) selection.DeselectAll( ) if changeSucceeded then print( "RBits-Wiggler") if useLowCIRules then local ciOld = behavior.GetClashImportance( ) ExactSetCI( 0.05 ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) ExactSetCI( ciOld ) structure.WiggleAll( 16 ) else ShakeOrMutate( SHAKESIDECHAIN_ITERS ) structure.WiggleAll( 2 ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) structure.WiggleAll( 16 ) end return true else return false end return changeSucceeded end function WiskyRebuildAllAction( ) SelectAllMovable( ) changeSucceeded = RebuildSelected( 1 ) if changeSucceeded then ShakeOrMutate( SHAKESIDECHAIN_ITERS ) structure.WiggleAll( 10 ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) LongWiggle( ) end return changeSucceeded end function RebuildAllWithIdealizeAction( ) SelectAllMovable( ) changeSucceeded = RebuildSelected( 1 ) if changeSucceeded then ShakeOrMutate( SHAKESIDECHAIN_ITERS ) structure.WiggleAll( 10 ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) IterativeWiggleAll( true, true ) lowCiIdealizer() end return changeSucceeded end ---------------------------------------------------------------------- -- FUNCTIONS TO PERFORM SOME ACTUAL ACTION ---------------------------------------------------------------------- function PerformRebuildRangeAction( idx ) if idx == nil or idx == 0 then idx = randomMovableSeg( ) end idxStart, idxEnd = GetRebuildRange( idx ) print( "RebuildRange on range "..idxStart..","..idxEnd.. " Idealize? "..BoolStr(REBUILD_ADDIDEALIZE) ) local maxFall = REBUILD_MAX_MEDIUM_SCOREDROP changeSucceeded = false local initScore = getQuickScore( ) changeSucceeded = RebuildInRange( idxStart, idxEnd, REBUILD_TRYCOUNT, maxFall ) if changeSucceeded then if REBUILD_ADDIDEALIZE then save.Quicksave( QS_ShortUseTemp2 ) -- so we can throw out the idealize if we don't like result local score = getQuickScore( ) local check = IdealizeInRange( idxStart, idxEnd, REBUILD_USECUTS ) if check and score < getQuickScore( ) then save.Quicksave( QS_ShortUseTemp2 ) score = getQuickScore( ) end save.Quickload( QS_ShortUseTemp2 ) end local idxListOuter = {} getSegsWithinRangeOfTube( idxListOuter, idxStart, idxEnd, REBUILD_OUTER_TUBE_RADIUS ) WiggleShakeWiggleList( idxListOuter ) end return changeSucceeded end function PerformRebuildSSAction( idx ) if idx == nil or idx == 0 then idx = randomMovableSeg( ) end -- This tries to get a range matching some Secondary Structure: idxStart, idxEnd = GetRegionForOperation( idx ) print( "RebuildSS on range " ..idxStart.. "," ..idxEnd.. " Idealize? "..BoolStr(REBUILD_ADDIDEALIZE)) changeSucceeded = false local initScore = getQuickScore( ) changeSucceeded = RebuildInRange( idxStart, idxEnd, REBUILD_TRYCOUNT, REBUILD_MAX_MEDIUM_SCOREDROP ) if changeSucceeded then if REBUILD_ADDIDEALIZE then -- use quickslot so we can throw out the idealize if we don't like result save.Quicksave( QS_ShortUseTemp2 ) local preScore = getQuickScore( ) local check = IdealizeInRange( idxStart, idxEnd, REBUILD_USECUTS ) if (not check) or preScore < getQuickScore( ) then -- throw away the idealize result save.Quickload( QS_ShortUseTemp2 ) end end local idxListOuter = {} getSegsWithinRangeOfTube( idxListOuter, idxStart, idxEnd, REBUILD_OUTER_TUBE_RADIUS ) WiggleShakeWiggleList( idxListOuter ) end return changeSucceeded end function PerformRebuildSimplePointAction( idx ) if idx == nil or idx == 0 then idx = randomMovableSeg( ) end print( "RebuildSimple on seg "..idx ) return RebuildSimple( idx, idx, REBUILD_ADDIDEALIZE ) end function PerformRebuildSimpleRangeAction( idx ) if idx == nil or idx == 0 then idx = randomMovableSeg( ) end idxStart, idxEnd = GetRebuildRange( idx ) print( "RebuildSimpleRange on range " ..idxStart.. "," ..idxEnd ) return RebuildSimple( idxStart, idxEnd, REBUILD_ADDIDEALIZE ) end function PerformRebuildSimple2PointAction( idx ) if idx == nil or idx == 0 then idx = randomMovableSeg( ) end print( "RebuildSimple2 on seg "..idx ) return RebuildSimple2( idx, idx ) end function PerformRebuildSimple2RangeAction( idx ) if idx == nil or idx == 0 then idx = randomMovableSeg( ) end idxStart, idxEnd = GetRebuildRange( idx ) print( "RebuildSimple2Range on range " ..idxStart.. "," ..idxEnd ) return RebuildSimple2( idxStart, idxEnd ) end function PerformRebuildSimple3RangeAction( idx ) if idx == nil then idx = randomMovableSeg( ) end idxStart, idxEnd = GetRebuildRange( idx ) print( "RebuildSimple3Range on range " ..idxStart.. "," ..idxEnd ) return RebuildSimple3( idxStart, idxEnd, randomBool( )) end function PerformRebuildFromQuakerAction( idx ) if idx == nil or idx == 0 then idx = randomMovableSeg( ) end idxStart, idxEnd = GetRebuildRange( idx ) print( "RebuildFromQuaker on range " ..idxStart.. "," ..idxEnd ) return RebuildSection_FromQuaker( idxStart, idxEnd, REBUILD_TRYCOUNT, REBUILD_MAX_MEDIUM_SCOREDROP ) end function PerformRebuildBitspawnAction( idx ) if idx == nil or idx == 0 then idx = randomMovableSeg( ) end idxStart, idxEnd = GetRebuildRange( idx ) print( "RebuildBitspawn on range " ..idxStart.. "," ..idxEnd ) return RebuildBitspawn( idxStart, idxEnd, false ) end function PerformRebuildBitspawnLowCI( idx ) if idx == nil or idx == 0 then idx = randomMovableSeg( ) end idxStart, idxEnd = GetRebuildRange( idx ) print( "RebuildBitspawnLowCI on range " ..idxStart.. "," ..idxEnd ) return RebuildBitspawn( idxStart, idxEnd, true ) end function PerformContactMapRebuildRangeAction( idx ) if idx == nil then idx = randomMovableSeg( ) end local len = random( REBUILD_MAX_SEGLEN ) if idx + len - 1 > segCt then len = segCt - idx + 1 end idxEnd = idx + len - 1 print( "ContactMapRebuildRange on range ".. idx, idxEnd ) changeSucceeded = PutContactMapBands( ) if changeSucceeded then if USE_LOWCI_WIGGLES then changeSucceeded = RebuildSimple3( idx, idxEnd, randomBool( )) elseif randomBool( ) then changeSucceeded = RebuildSimple( idx, idxEnd ) else changeSucceeded = RebuildSimple2( idx, idxEnd ) end end DelBands() qStabWiggle( randomBool(PROB_QSTAB_EXTRA)) return changeSucceeded end -- think about all-loop and restoring secondary structure and such function PerformContactMapRebuildSSAction( idx ) if idx == nil then idx = randomMovableSeg( ) end local idxStart, idxEnd = GetRegionForOperation( idx ) print( "ContactMapRebuildSS for range "..idxStart..","..idxEnd ) 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 = PutContactMapBands( ) if changeSucceeded then if randomBool( ) then changeSucceeded = RebuildInRange( idxStart, idxEnd, REBUILD_TRYCOUNT, REBUILD_MAX_MEDIUM_SCOREDROP ) else changeSucceeded = RebuildSection_FromQuaker( idxStart, idxEnd, REBUILD_TRYCOUNT, REBUILD_MAX_MEDIUM_SCOREDROP ) end end REBUILD_INNER_TUBE_RADIUS = oldInnerTube REBUILD_OUTER_TUBE_RADIUS = oldOuterTube DelBands() qStabWiggle( randomBool(PROB_QSTAB_EXTRA)) return changeSucceeded end function PerformContactMapRebuildAllAction( ) print( "ContactMapRebuildAll" ) changeSucceeded = PutContactMapBands( ) if changeSucceeded then WiskyRebuildAllAction( ) end DelBands() qStabWiggle( true ) return changeSucceeded end function PerformIdealizeRangeAction( idx ) if idx == nil then idx = randomMovableSeg( ) end local maxSeg = math.min( segCt, idx + random( IDEALIZE_MAX_SEGLEN ) ) local idxStart, idxEnd = GetRegionForOperation( idx ) if idxEnd - idxStart + 1 > IDEALIZE_MAX_SEGLEN then if idx - idxStart + 1 > IDEALIZE_MAX_SEGLEN then idxStart = math.max( 1, idx - IDEALIZE_MAX_SEGLEN) end end if idxEnd - idxStart + 1 > IDEALIZE_MAX_SEGLEN then idxEnd = idxStart + IDEALIZE_MAX_SEGLEN - 1 end print( "IdealizeRange for ".. idxStart..","..idxEnd ) changeSucceeded = IdealizeInRange( idxStart, idxEnd, IDEALIZE_USE_CUTS ) qStabWiggle( randomBool( PROB_QSTAB_EXTRA) ) return changeSucceeded end function PerformIdealizePointAction( idx ) if idx == nil then idx = randomMovableSeg( ) end print( "IdealizePoint for ".. idx ) changeSucceeded = IdealizeInRange( idx, idx, IDEALIZE_USE_CUTS ) qStabWiggle( randomBool( PROB_QSTAB_EXTRA )) return changeSucceeded end function PerformSingleBandInSpaceAction( idx ) if idx == nil then idx = randomMovableSeg( ) end 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 RegionWiggleBeforeAndAfter( idx, idx ) end return changeSucceeded end function PerformBandToRandomSegAction( idx ) if idx == nil then idx = randomMovableSeg( ) end local strength = BAND_STRENGTH_DEFAULT local atom = PickAtomNumberForBand( idx ) local doVariableStrength = true -- print is done here because PutBandToRandomSeg is called by other pickers print( "BandBetween for segment " .. idx.." with strength "..TrimNum( strength ) ) changeSucceeded = PutBandToRandomSeg( idx, strength, atom ) if changeSucceeded then PickBeforeAndAfterWiggler( doVariableStrength ) end return changeSucceeded end function PerformPushPullAction( idx ) if idx == nil then idx = randomMovableSeg( ) end local segList = {} local wantPushNotPull = randomBool( ) local ctBands = 5 local strength = BAND_STRENGTH_DEFAULT local doVariableStrength = true print( "PushPull for segment " .. idx ) 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) ] changeSucceeded = PutPushPullBands( idx, ctBands, centerSeg, strength, wantPushNotPull ) if changeSucceeded then PickBeforeAndAfterWiggler( doVariableStrength ) end return changeSucceeded end function PerformVoidCrusherAction( idx ) if idx == nil then idx = randomMovableSeg( ) end local strength = BAND_STRENGTH_DEFAULT local doVariableStrength = true print( "VoidCrusher for segment " .. idx ) changeSucceeded = PutVoidCrusherBands( idx, strength ) if changeSucceeded then PickBeforeAndAfterWiggler( doVariableStrength ) end return changeSucceeded end function PerformGlycineHingeAction( idx ) if idx == nil then idx = randomAASeg( ) end if idx == 0 then return false end local strength = random( 0.25, BAND_STRENGTH_DEFAULT, true ) print( "GlycineHinge for idx="..idx) local startSeg = math.max( 1, idx - 4 - random( 0, 2 )) local endSeg = math.min( segCt, idx + 4 + random( 0, 2 )) changeSucceeded = PutGlycineHingeBands( idx, strength ) if changeSucceeded then SetCI( REGIONMOD_CI ) -- had been SimpleRegionWiggle, but seemed to be doing nothing RegionWiggleBeforeAndAfter( startSeg, endSeg ) end return changeSucceeded end function PerformGeneralHingeAction( 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 print( "GeneralHinge for range "..startSeg..","..endSeg) changeSucceeded = PutGeneralHingeBands( startSeg, endSeg, strength ) if changeSucceeded then SetCI( REGIONMOD_CI ) -- had been SimpleRegionWiggle, but seemed to be doing nothing RegionWiggleBeforeAndAfter( startSeg, endSeg ) end return changeSucceeded end function PerformRotatorAction( idx ) if idx == nil then idx = randomMovableSeg( ) end local strength = BAND_STRENGTH_REGION local bisLen = BIS_LENGTH local goClockwise = randomBool() local doubleRotate = randomBool() local startSeg, endSeg = GetRegionForOperation( idx ) print( "Rotator for range "..startSeg..","..endSeg) if endSeg - startSeg <= 1 then return false end -- something's very wrong -- Put the bands, use wiggle to do a rotation changeSucceeded = PutRotatorBands( startSeg, endSeg, bisLen, strength, goClockwise ) if changeSucceeded then SetCI( REGIONMOD_CI ) SimpleRegionWiggle( startSeg, endSeg, false, 2 + random( 4 ) ) end DelBands( ) if doubleRotate then changeSucceeded = PutRotatorBands( startSeg, endSeg, bisLen, strength, goClockwise ) if changeSucceeded then SetCI( REGIONMOD_CI ) SimpleRegionWiggle( startSeg, endSeg, false, 4 + random( 3 ) ) end end DelBands( ) CleanShakeQStab() return changeSucceeded end function PerformPusherAction( idx ) if idx == nil then idx = randomMovableSeg( ) end local strength = BAND_STRENGTH_REGION local bisLen = BIS_LENGTH_LONG local goForward = randomBool() local doublePush = randomBool() local startSeg, endSeg = GetRegionForOperation( idx ) print( "Pusher for range "..startSeg..","..endSeg) if endSeg - startSeg <= 1 then return false end -- something's very wrong changeSucceeded = PutPusherBands( startSeg, endSeg, bisLen, strength, goForward ) if changeSucceeded then SetCI( REGIONMOD_CI ) SimpleRegionWiggle( startSeg, endSeg, false, 2 + random( 4 ) ) end DelBands( ) if doublePush then changeSucceeded = PutPusherBands( startSeg, endSeg, bisLen, strength, goForward ) if changeSucceeded then SetCI( REGIONMOD_CI ) SimpleRegionWiggle( startSeg, endSeg, false, 4 + random( 3 ) ) end end DelBands( ) CleanShakeQStab() return changeSucceeded end function PerformMoverAction( idx ) if idx == nil then idx = randomMovableSeg( ) end local strength = BAND_STRENGTH_REGION local bisLen = BIS_LENGTH_LONG local doubleMove = randomBool() local startSeg, endSeg = GetRegionForOperation( idx ) print( "Mover for range "..startSeg..","..endSeg) if endSeg - startSeg <= 1 then return false end -- something's very wrong changeSucceeded = PutMoverBands( startSeg, endSeg, bisLen, strength ) if changeSucceeded then SetCI( REGIONMOD_CI ) SimpleRegionWiggle( startSeg, endSeg, false, 2 + random( 4 ) ) end DelBands( ) if doubleMove then changeSucceeded = PutMoverBands( startSeg, endSeg, bisLen, strength ) if changeSucceeded then SetCI( REGIONMOD_CI ) SimpleRegionWiggle( startSeg, endSeg, false, 4 + random( 3 ) ) end end DelBands( ) CleanShakeQStab() return changeSucceeded end function PerformFlexerAction( idx ) if idx == nil then idx = randomMovableSeg( ) end local strength = BAND_STRENGTH_REGION local bisLen = BIS_LENGTH local doubleFlex = randomBool() local startSeg, endSeg = GetRegionForOperation( idx ) print( "Flexer for range "..startSeg..","..endSeg) if endSeg - startSeg <= 1 then return false end -- something's very wrong changeSucceeded = PutFlexerBands( startSeg, endSeg, bisLen, strength ) if changeSucceeded then SetCI( REGIONMOD_CI ) SimpleRegionWiggle( startSeg, endSeg, false, 2 + random( 4 ) ) end DelBands( ) if doubleFlex then changeSucceeded = PutFlexerBands( startSeg, endSeg, bisLen, strength ) if changeSucceeded then SetCI( REGIONMOD_CI ) SimpleRegionWiggle( startSeg, endSeg, false, 4 + random( 3 ) ) end end DelBands( ) CleanShakeQStab() return changeSucceeded end function PerformSheetStraightenerAction( idx ) if idx == nil then idx = randomMovableSeg( ) end local strength = BAND_STRENGTH_REGION local startSeg, endSeg = GetRegionForOperation( idx ) print( "SheetStraightener for range "..startSeg..","..endSeg) if endSeg - startSeg <= 1 then return false end -- something's very wrong changeSucceeded = PutSheetStraightenerBands( startSeg, endSeg, strength ) if changeSucceeded then SetCI( REGIONMOD_CI ) SimpleRegionWiggle( startSeg, endSeg, false, 2 + random( 4 ) ) DelBands( ) CleanShakeQStab() end return changeSucceeded end function PerformHelixUniformAction( idx ) if idx == nil then idx = randomMovableSeg( ) end local strength = BAND_STRENGTH_REGION local startSeg, endSeg = GetRegionForOperation( idx ) print( "HelixUniform for range "..startSeg..","..endSeg) if endSeg - startSeg <= 1 then return false end -- something's very wrong local regionLength = endSeg - startSeg + 1 local hShift = 4 if randomBool( 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( 2, 4 ) end if endSeg < idx + 2 then endSeg = idx + random( 2, 4 ) end changeSucceeded = PutHelixUniformDistanceBands( startSeg, endSeg, hShift, strength ) if changeSucceeded then SetCI( REGIONMOD_CI ) SimpleRegionWiggle( startSeg, endSeg, false, 2 + random( 4 ) ) DelBands( ) CleanShakeQStab() end return changeSucceeded end function PerformHelixCompressorAction( 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 ) print( "HelixCompressor for range "..startSeg..","..endSeg) if endSeg - startSeg <= 1 then return false end -- something's very wrong local regionLength = endSeg - startSeg + 1 local hShift = 4 if randomBool( 0.25 ) then hShift = math.min(regionLength - 2, randomDice(4, 1, 2)) -- a number 4 - 8, preferring 6ish end changeSucceeded = PutHelixCompressorBands( startSeg, endSeg, hShift, strength, hComp ) if changeSucceeded then SetCI( REGIONMOD_CI ) SimpleRegionWiggle( startSeg, endSeg, false, 2 + random( 4 ) ) DelBands( ) CleanShakeQStab() end return changeSucceeded end function PerformHelixFixerAction( idx ) if idx == nil then idx = randomMovableSeg( ) end local strength = BAND_STRENGTH_REGION local do4 = randomBool( ) local startSeg, endSeg = GetRegionForOperation( idx ) print( "HelixFixer for range "..startSeg..","..endSeg) if endSeg - startSeg <= 1 then return false end -- something's very wrong changeSucceeded = PutHelixFixerBands( startSeg, endSeg, strength, do4 ) if changeSucceeded then SetCI( REGIONMOD_CI ) SimpleRegionWiggle( startSeg, endSeg, false, 2 + random( 4 ) ) DelBands( ) CleanShakeQStab() end return changeSucceeded end function PerformSheetSewerAction( ) local maxSheetDistance = random( 4.0, 12.0, true ) -- default choice was 8.0 local strength = BAND_STRENGTH_DEFAULT print( "SheetSewer") changeSucceeded = PutSheetSewerBandsAllOver( maxSheetDistance, strength ) if changeSucceeded then SetCI( REGIONMOD_CI ) WiggleShakeCleanQStab( ) end return changeSucceeded end function PerformMultiBanderAction( ) 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 local doVariableStrength = true print( "MultiBander with bands="..ctBandsToMake ) while bandCt < ctBandsToMake do local which = random( 2 ) if which == 1 then local seg = randomMovableSeg( ) local maxRho = 1.0 if PutBandInSpace( seg, maxRho, strength ) then changeSucceeded = true bandCt = bandCt + 1 end else local idx = randomMovableSeg( ) local atom = PickAtomNumberForBand( idx ) if PutBandToRandomSeg( idx, strength, atom ) then bandCt = bandCt + 1 end end end if changeSucceeded then PickBeforeAndAfterWiggler( doVariableStrength ) end return changeSucceeded end function PerformCentralBanderAction( ) local strength = BAND_STRENGTH_DEFAULT local maxCenters = randomDice( 2, 1, 3) changeSucceeded = false local doVariableStrength = true 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) print( "CentralBander with bands="..ctBandsToMake ) changeSucceeded = PutCoreCompressBands( coreSegs, skipCt, startOffset, strength, compression, randomBool( ), randomBool( ) ) if changeSucceeded then PickBeforeAndAfterWiggler( doVariableStrength ) end return changeSucceeded end function PerformCoreStirrerAction( ) -- not well suited to a lowCI situation changeSucceeded = false print( "CoreStirrer" ) 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 = randomBool( ) local doSidechains = randomBool( ) getCentralHydrophobics( thisCore, maxCenters ) changeSucceeded = PutCoreCompressBands( thisCore, skipCt, startOffset, strength, expansion, pullPhilics, doSidechains ) if not changeSucceeded then return false end print( "BandedWiggleUntilEnoughChange" ) BandedWiggleUntilEnoughChange( 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( true ) -- complete action of SimpleWiggleShakeWrapper now! return true end -- THIS one is normal band operation: deletes bands then wiggles out function PerformEveryXToEveryYBandAction( ) local compr compr = random( BAND_MINCOMPRESS, BAND_MAXEXPAND, true ) 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 ) print( "BandEveryXToY for X,Y=" ..Xinterval.. "," ..Yinterval.. " starting " ..Xinit.. "," ..Yinit ) changeSucceeded = PutBandsEveryXToEveryY( Xinterval , Xinit, Yinterval, Yinit, strength, compr ) if changeSucceeded then BandedWiggleUntilEnoughChange( BANDEDWIGGLE_TARGET, COREPULLING_CI ) end DelBands( ) qStabWiggle( randomBool( PROB_QSTAB_EXTRA )) return changeSucceeded end -- THIS ONE IS SPECIAL: it is called whenever we want to pre-expand our protein function PerformEveryXToEveryYExpansion( ) local compr = random( BAND_MINEXPAND, BAND_MAXEXPAND, true ) 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.5, 1.50, true ) -- FIND OUT: is always strong good? print("XToYExpander: " ..Xinterval.. "x" ..Yinterval.. " expand="..TrimNum(compr).. " strength="..TrimNum(strength)) local initScore = getScore() changeSucceeded = PutBandsEveryXToEveryY( Xinterval , Xinit, Yinterval, Yinit, strength, compr ) if changeSucceeded then SimpleWiggleShake( ) end DelBands( ) local endScore = getScore() print("Expander Score drop = "..TrimNum(endScore-initScore)) return changeSucceeded end function PerformGentleCompAction( ) 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 local doVariableStrength = true print( "GentleComp for " ..bandsWanted.. " bands" ) changeSucceeded = PutGentleCompBands( bandsWanted, minLength, maxLength, minSeparation, strength, compression ) if changeSucceeded then PickBeforeAndAfterWiggler( doVariableStrength ) end return changeSucceeded end function PerformGentleCompLowCIAction( ) 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 = 15 local compression = 1.0 if randomBool() then compression = random( BAND_MINCOMPRESS, 0.90, true ) else compression = random( 1.10, BAND_MAXEXPAND, true ) end local bandsWanted = 3 local strength = BAND_STRENGTH_DEFAULT local doVariableStrength = true print( "GentleCompLowCI for " ..bandsWanted.. " bands" ) changeSucceeded = PutGentleCompBands( bandsWanted, minLength, maxLength, minSeparation, strength, compression ) if changeSucceeded then PickBeforeAndAfterWiggler( doVariableStrength ) end return changeSucceeded end function PerformLSQuakeAction( ) local idx = randomSeg( ) -- we don't care if it's movable local wantFreeze = randomBool( ) 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 local doVariableStrength = true ResetFrozenness( ) local startIdx, endIdx = GetRegionForOperation( idx ) print( "LSQuaker 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 PickBeforeAndAfterWiggler( doVariableStrength ) end return changeSucceeded end function PerformTailWhipAction( idx ) if idx == nil then fromStart = randomBool( ) end if idx < segCt / 2 then fromStart = true else fromStart = false end length = 0 local r = randomBool() if r then if fromStart then idx = 1 else idx = segCt end st,ed = GetRegionForOperation( idx ) length = ed - st + 1 end if length < 5 then length = random( 5,15 ) end if fromStart then str = "start" else str = "end" end print( "TailWhip at " .. str .. " length="..length ) selection.DeselectAll( ) s = 1 e = length if fromStart then changeSucceeded = PutTailDetacherBands( true, length ) else s = segCt - length + 1 e = segCt changeSucceeded = PutTailDetacherBands( false, length ) end selection.SelectRange( s, e ) if not changeSucceeded then return false end SimpleRegionWiggle( s, e, false, random(4) ) -- pull the tail away from the main body DelBands( ) selection.DeselectAll( ) if fromStart then changeSucceeded = PutTailAttacherBands( true, length ) else changeSucceeded = PutTailAttacherBands( false, length ) end selection.SelectRange( s, e ) if not changeSucceeded then return false end SimpleRegionWiggle( s, e, false, random(4) ) -- bring the tail back to the main body selection.DeselectAll( ) DelBands( ) -- do our own "after" wiggle recentbest.Save( ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) IterativeWiggleAll( true, true ) recentbest.Restore( ) return true end function PerformSingleContactMapAction( idx ) if idx == nil then idx = randomContactableSeg( ) end print( "SingleContactMapBand at ".. idx ) changeSucceeded = false local heatThreshold = CONTACTMAP_THRESHOLD local strength = 1.5 * BAND_STRENGTH_DEFAULT local doVariableStrength = true changeSucceeded = PutSomeContactMapBands( heatThreshold, strength, 1, randomBool( ), true ) if changeSucceeded then PickBeforeAndAfterWiggler( doVariableStrength ) end return changeSucceeded end function PerformSomeContactMapBandsAction( ) print( "SomeContactMapBands" ) changeSucceeded = false local heatThreshold = CONTACTMAP_THRESHOLD local strength = BAND_STRENGTH_DEFAULT local ctWanted = random( 5, 10 ) local doVariableStrength = true changeSucceeded = PutSomeContactMapBands( heatThreshold, strength, ctWanted, randomBool( ), true ) if changeSucceeded then PutRandomCompBands() PickBeforeAndAfterWiggler( doVariableStrength ) end return changeSucceeded end function PerformContactMapBandsForSegAction( idx ) print( "ContactMapBandsForSeg for idx " ..idx ) if idx == nil then idx = randomContactableSeg( ) end changeSucceeded = false local heatThreshold = CONTACTMAP_THRESHOLD local strength = BAND_STRENGTH_DEFAULT local doSidechain = false local doVariableStrength = false if BAND_TO_SIDECHAINS then doSidechain = randomBool( ) end changeSucceeded = PutAllContactMapBandsToSeg( idx, heatThreshold, strength, true, doSidechain, true ) if changeSucceeded then PutRandomCompBands() PickBeforeAndAfterWiggler( doVariableStrength ) end return changeSucceeded end function PerformCMTwoRegionBandAction( idx1 ) print( "CMTwoRegionBander for idx " ..idx1 ) if idx1 == nil then idx1 = randomContactableSeg( ) end local strength = BAND_STRENGTH_DEFAULT local heatThreshold = CONTACTMAP_THRESHOLD local doVariableStrength = false 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 ) changeSucceeded = PutContactMapBandsBetweenRegions( heatThreshold, startIdx1, endIdx1, startIdx2, endIdx2, strength, true, randomBool( ), randomBool( ), true ) if changeSucceeded then PutRandomCompBands() PickBeforeAndAfterWiggler( doVariableStrength ) end return changeSucceeded end function PerformContactMapperAction( ) print("ContactMapper" ) changeSucceeded = PutContactMapBands( ) if changeSucceeded then PutRandomCompBands() PickBeforeAndAfterWiggler( doVariableStrength ) end return changeSucceeded end function PerformPushPullContactMapAction( badSeg ) if badSeg == nil then badSeg = randomContactableSeg( ) end local segList = {} local wantPushNotPull = randomBool( ) local ctBands = 5 local strength = BAND_STRENGTH_DEFAULT local doVariableStrength = true print("PushPullCM at seg "..badSeg ) 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 ) ] changeSucceeded = PutPushPullContactMapBands( badSeg, ctBands, centerSeg, strength, CONTACTMAP_THRESHOLD, wantPushNotPull ) if changeSucceeded then PickBeforeAndAfterWiggler( doVariableStrength ) end return changeSucceeded end function PerformVoidCrusherContactMapAction( idx ) if idx == nil then idx = randomContactableSeg( ) end local strength = BAND_STRENGTH_DEFAULT local doVariableStrength = true print("VoidCrusherCM at seg "..idx ) changeSucceeded = PutVoidCrusherContactMapBands( idx, strength, CONTACTMAP_THRESHOLD ) if changeSucceeded then PickBeforeAndAfterWiggler( doVariableStrength ) end return changeSucceeded end -- generally not used due to very expensive sequence of actions function PerformLocalQuakeAction( idx ) if idx == nil then idx = randomMovableSeg( ) end print( "LocalQuake at ".. idx ) 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 ShakeWiggleCleanShakeWiggle( ) else SimpleWiggleShakeWrapper( 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 ) return changeSucceeded end function PerformMutatorAction( idx ) if idx == nil then idx = randomMovableSeg( ) end -- better would be random mutable seg print( "SpotMutator for segment " .. idx ) if randomBool( PROB_MUTATESIMILAR ) then changeSucceeded = MutateToSimilar( idx ) else changeSucceeded = MutateToAny( idx ) end PickBeforeAndAfterWiggler( false ) return changeSucceeded end -------------------------------------------------------------------------------------------------------- -- NEIGHBOR-GENERATION -------------------------------------------------------------------------------------------------------- function PickActionFromList( allowedActions, idx ) local isRebuild = false local didExpand = false algName = ChooseAlgorithm( allowedActions ) if PERFORM_CYSTEINE_BANDING then -- decide which cysteine bands to enable and which to disable EnableAndDisableCysteineBands( ) end -- decide whether to perform expansion if EXPAND_BEFORE_ACTION and randomBool( PROB_USE_EXPANDER ) then PerformEveryXToEveryYExpansion() didExpand = true end -- non-band actions (rebuild, idealize, mutate) if algName == "SpotMutator" then changeSucceeded = PerformMutatorAction( idx ) elseif algName == "IdealizePoint" then changeSucceeded = PerformIdealizePointAction( idx ) elseif algName == "IdealizeInRange" then changeSucceeded = PerformIdealizeRangeAction( idx ) elseif algName == "RebuildSS" then changeSucceeded = PerformRebuildSSAction( idx ) isRebuild = true elseif algName == "RebuildInRange" then changeSucceeded = PerformRebuildRangeAction( idx ) isRebuild = true elseif algName == "RebuildSimplePoint" then changeSucceeded = PerformRebuildSimplePointAction( idx ) isRebuild = true elseif algName == "RebuildSimpleRange" then changeSucceeded = PerformRebuildSimpleRangeAction( idx ) isRebuild = true elseif algName == "RebuildSimple2Point" then changeSucceeded = PerformRebuildSimple2PointAction( idx ) isRebuild = true elseif algName == "RebuildSimple2Range" then changeSucceeded = PerformRebuildSimple2RangeAction( idx ) isRebuild = true elseif algName == "RebuildSimple3Range" then changeSucceeded = PerformRebuildSimple3RangeAction( idx ) isRebuild = true elseif algName == "RebuildFromQuaker" then changeSucceeded = PerformRebuildFromQuakerAction( idx ) isRebuild = true elseif algName == "RebuildBitspawn" then changeSucceeded = PerformRebuildBitspawnAction( idx ) isRebuild = true elseif algName == "RebuildBitspawnLowCI" then changeSucceeded = PerformRebuildBitspawnLowCI( idx ) isRebuild = true -- simple-task banders elseif algName == "BIS" then changeSucceeded = PerformSingleBandInSpaceAction( idx ) elseif algName == "BandBetween" then changeSucceeded = PerformBandToRandomSegAction( idx ) elseif algName == "RotatorBands" then changeSucceeded = PerformRotatorAction( idx ) elseif algName == "PusherBands" then changeSucceeded = PerformPusherAction( idx ) elseif algName == "MoverBands" then changeSucceeded = PerformMoverAction( idx ) elseif algName == "FlexerBands" then changeSucceeded = PerformFlexerAction( idx ) elseif algName == "SheetStraightener" then changeSucceeded = PerformSheetStraightenerAction( idx ) elseif algName == "HelixUniform" then changeSucceeded = PerformHelixUniformAction( idx ) elseif algName == "HelixCompressor" then changeSucceeded = PerformHelixCompressorAction( idx ) elseif algName == "HelixFixer" then changeSucceeded = PerformHelixFixerAction( idx ) elseif algName == "SheetSewer" then changeSucceeded = PerformSheetSewerAction( idx ) elseif algName == "GeneralHinge" then changeSucceeded = PerformGeneralHingeAction( idx ) elseif algName == "GlycineHinge" then changeSucceeded = PerformGlycineHingeAction( idx ) elseif algName == "TailWhip" then changeSucceeded = PerformTailWhipAction( idx ) elseif algName == "PushPull" then changeSucceeded = PerformPushPullAction( idx ) elseif algName == "VoidCrusher" then changeSucceeded = PerformVoidCrusherAction( idx ) -- large-scale banders elseif algName == "MultiBander" then changeSucceeded = PerformMultiBanderAction( ) elseif algName == "CentralBander" then changeSucceeded = PerformCentralBanderAction( ) elseif algName =="EveryXToY" then changeSucceeded = PerformEveryXToEveryYBandAction( ) elseif algName == "GentleComp" then changeSucceeded = PerformGentleCompAction( ) elseif algName == "GentleCompLowCI" then changeSucceeded = PerformGentleCompLowCIAction( ) elseif algName == "CoreStirrer" then changeSucceeded = PerformCoreStirrerAction( ) elseif algName == "LSQuaker" then changeSucceeded = PerformLSQuakeAction( ) elseif algName == "LocalQuake" then changeSucceeded = PerformLocalQuakeAction( idx ) -- special contact map actions elseif algName == "ContactMapper" then changeSucceeded = PerformContactMapperAction( ) elseif algName == "ContactMapTwoRegions" then changeSucceeded = PerformCMTwoRegionBandAction( idx ) elseif algName == "ContactTwoSegs" then changeSucceeded = PerformSingleContactMapAction( idx ) elseif algName == "SomeContactMapBands" then changeSucceeded = PerformSomeContactMapBandsAction( ) elseif algName == "ContactMapForSeg" then changeSucceeded = PerformContactMapBandsForSegAction( idx ) elseif algName == "PushPullContactMap" then changeSucceeded = PerformPushPullContactMapAction( idx ) elseif algName == "VoidCrusherContactMap" then changeSucceeded = PerformVoidCrusherContactMapAction( idx ) elseif algName == "ContactMapRebuildRange" then changeSucceeded = PerformContactMapRebuildRangeAction( idx ) isRebuild = true elseif algName == "ContactMapRebuildSS" then changeSucceeded = PerformContactMapRebuildSSAction( idx ) isRebuild = true elseif algName == "ContactMapRebuildAll" then changeSucceeded = PerformContactMapRebuildAllAction( ) isRebuild = true else print( "Error - invalid action: ".. algName ) return false end -- cleanup on aisle 6 ResetFrozenness( ) DelBands( ) ResetCuts( ) selection.DeselectAll( ) SetCI( 1.0 ) -- A bit of record-keeping for failures if not changeSucceeded then -- heavily immovable proteins can fail rebuilds forever. -- We need to be able to bail, so increment global failure variable if isRebuild then rebuild_fails = rebuild_fails + 1 print( "CHANGE FAILED - RB(" .. rebuild_fails .. ")" ) else print( "CHANGE FAILED - " .. algName ) end return false end -- closeout wiggles and such. if didExpand then algName = "Exp" .. algName -- TEMPORARY -- undo our extra action at the start of it all recentbest.Save( ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) recentbest.Restore( ) LongWiggle( ) recentbest.Restore( ) end -- Always do a closeout Shake/Mutate + LongWiggle recentbest.Save( ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) recentbest.Restore( ) LongWiggle( ) recentbest.Restore( ) -- if a fuse is going to happen, now is the time if USE_FUSEWIGGLE then DoFuseWiggle( ) end return changeSucceeded end ---------------------------------------------------------------- -- MULTITRY FUNCTIONS ---------------------------------------------------------------- function SaveTopBests( TopBestSlots ) -- todo: consider demanding a pose be significantly different -- before adding (if not, but better score, then just replace) save.Quicksave( QS_Swapslot ) currScore = getScore() for i = 1, #TopBestSlots do save.Quickload( TopBestSlots[ i ] ) savScore = getScore() if currScore > savScore then for j=#TopBestSlots-1, i, -1 do save.Quickload( TopBestSlots[j] ) save.Quicksave( TopBestSlots[j+1] ) end save.Quickload( QS_Swapslot ) save.Quicksave( TopBestSlots[ i ] ) -- save new pose break end end save.Quickload( QS_Swapslot ) end function GetBestFromList( TopBests, wantReport ) -- it's already sorted, so we'll load the first one if wantReport then for i=1, #TopBests do save.Quickload( TopBests[ i ] ) print( "Score #" .. i .. " = " .. getScore() ) end end save.Quickload( TopBests[1] ) end function SetupTopBests( TopBests, NumberOfSlotsWanted ) if NumberOfSlotsWanted > QS_MultiStartSaveLast - QS_MultiStartSave1 + 1 then print( "!!! Asked for more slots than allowed max" ) return false end for i=1, NumberOfSlotsWanted do TopBests[#TopBests+1] = QS_MultiStartSave1 + i - 1 save.Quicksave( QS_MultiStartSave1 + i - 1) end return true end function PerformMultiTryOperation( NumberOfStarts, NumberOfActionsPerTry, startIdx, endIdx, ActionFunction, abbrBase ) if abbrBase == nil then abbrBase = "" end print( "PerformMultiTryOperation for "..NumberOfStarts.." starts with ".. NumberOfActionsPerTry.." actions per try" ) BestScore=getScore( ) LoadBest( ) bests = {} TopBests = {} if not SetupTopBests( TopBests, NumberOfStarts ) then return false end save.Quicksave( QS_TopLevelTempMT ) save.Quicksave( QS_TopLevelTempM2 ) for k=1, NumberOfStarts do CurrentStart = k save.Quickload( QS_TopLevelTempMT ) -- always start over from initial state SaveBest( true, QS_TopLevelTempM2 ) -- best so far --print( "Starting try " .. k.. " of " .. NumberOfStarts .. ": Score="..TrimNum(getScore( ))) local initscore = getScore( ) for j=1, NumberOfActionsPerTry do if NumberOfActionsPerTry > 1 then abbr = abbrBase .. "-MT" .. NumberOfStarts .. "." .. NumberOfActionsPerTry.. ":" .. k .. "." .. j else abbr = abbrBase .. "-MT" .. NumberOfStarts .. ":" .. k end save.Quickload( QS_TopLevelTempM2 ) local currScore = getScore( ) ActionFunction( startIdx, endIdx, QS_TopLevelTempM2, abbr ) -- I expect ActionFunction to store its best result in QS..M2, -- but I do not expect it to load that result for me. save.Quickload( QS_TopLevelTempM2 ) local endScore = getScore( ) if endScore > currScore then save.Quicksave( QS_TopLevelTempM2 ) currScore = endScore end if endScore > BestScore then BestScore = endScore end SaveBest( false, QS_TopLevelTempM2 ) SaveBest( ) end -- FOR j save.Quickload( QS_TopLevelTempM2 ) print( abbr .. ": Finishing - Score: "..getScore( ) ) SaveTopBests( TopBests ) end -- FOR k GetBestFromList( TopBests, true ) print( "Taking result with score " .. TrimNum( getScore() )) return true end ---------------------------------------------------------------------- -- SCRIPT-SPECIFIC FUNCTIONS ---------------------------------------------------------------------- ---------------------------------------------------------------------- -- ROUTINES FOR CHOOSING ACTION COLLECTIONS ---------------------------------------------------------------------- function AddRegionBanderActionsToList(allowedActions, idx ) 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' or isPossibleHelix( idx ) -- Decide what other actions make sense to attempt if (not isHelixRegion) and idx >= 5 and idx <= segCt - 5 then -- GeneralHinge tends to be useless allowedActions[ #allowedActions + 1 ] = "GeneralHinge" end if (idx > 3) and (idx < segCt-3) and (structure.GetAminoAcid( idx ) == 'g') then allowedActions[ #allowedActions + 1 ] = "GlycineHinge" end if isHelixRegion then -- rotator is just another "mover" if aas are not coiled as in a helix allowedActions[ #allowedActions + 1 ] = "RotatorBands" if regionLength > 6 then -- actions that only make sense for helices -- (not just "curls" of helix-ness) allowedActions[ #allowedActions + 1] = "HelixUniform" allowedActions[ #allowedActions + 1] = "HelixCompressor" allowedActions[ #allowedActions + 1] = "HelixFixer" end end if isSheetRegion and #allSheets > 1 then -- SheetStraightener never turns out well... -- allowedActions[ #allowedActions + 1 ] = "SheetStraightener" allowedActions[ #allowedActions + 1] = "SheetSewer" end if regionLength >= 5 and idx >= 4 and idx <= segCt - 4 then allowedActions[ #allowedActions + 1] = "FlexerBands" end return end function PickMidgameAction( idx ) if randomBool( PROB_TARGETED ) then idx = randomLowScoringSeg( random( segCt/5 )) else if idx == nil or idx == 0 then idx = randomMovableSeg( ) end end if idx == nil then idx = randomMovableSeg( ) end local allowedActions = { "PushPull", "VoidCrusher", "GentleComp" } if (idx > 3) and (idx < segCt-3) and (structure.GetAminoAcid( idx ) == 'g') then allowedActions[ #allowedActions + 1 ] = "GlycineHinge" end if #NonLockedSegList > 0 then allowedActions[ #allowedActions + 1] = "RebuildInRange" allowedActions[ #allowedActions + 1] = "RebuildFromQuaker" allowedActions[ #allowedActions + 1] = "IdealizeInRange" end AddRegionBanderActionsToList( allowedActions, idx ) 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] = "ContactTwoSegs" if SegHasContactData( idx, CONTACTMAP_THRESHOLD ) then allowedActions[ #allowedActions + 1] = "ContactMapForSeg" end end return PickActionFromList( allowedActions, idx ) end function PickEarlyAction( idx ) if idx == nil or idx == 0 then idx = randomMovableSeg( ) end local allowedActions = { } allowedActions[ #allowedActions + 1] = "GentleComp" allowedActions[ #allowedActions + 1] = "PushPull" allowedActions[ #allowedActions + 1] = "GentleCompLowCI" allowedActions[ #allowedActions + 1] = "VoidCrusher" if #NonLockedSegList > 0 then allowedActions[ #allowedActions + 1] = "RebuildSimpleRange" allowedActions[ #allowedActions + 1] = "RebuildBitspawn" allowedActions[ #allowedActions + 1] = "RebuildBitspawnLowCI" end AddRegionBanderActionsToList( allowedActions, idx ) -- push, move, flex, helix, sheet if PuzzleHasContactMap then allowedActions[ #allowedActions + 1] = "ContactMapper" allowedActions[ #allowedActions + 1] = "SomeContactMapBands" allowedActions[ #allowedActions + 1] = "ContactMapTwoRegions" if #NonLockedSegList > 0 then allowedActions[ #allowedActions + 1] = "ContactMapRebuildRange" end if SegHasContactData( idx, CONTACTMAP_THRESHOLD ) then allowedActions[ #allowedActions + 1] = "PushPullContactMap" allowedActions[ #allowedActions + 1] = "VoidCrusherContactMap" allowedActions[ #allowedActions + 1] = "ContactMapForSeg" end end return PickActionFromList( allowedActions, idx ) end function PickRebuildAction( idx ) changeSucceeded = false if idx == nil or idx == 0 then idx = randomMovableSeg( ) end local allowedActions = { "RebuildInRange", "RebuildSimpleRange", "RebuildSimple2Range", "RebuildSimple3Range", "RebuildFromQuaker", "RebuildBitspawn", "RebuildBitspawnLowCI", "RebuildSS" } return PickActionFromList( allowedActions, idx ) end function PickLowCIAction( idx ) if idx == nil or idx == 0 then idx = randomMovableSeg( ) end local allowedActions = { } allowedActions[ #allowedActions + 1] = "GentleCompLowCI" allowedActions[ #allowedActions + 1] = "VoidCrusher" if #NonLockedSegList > 0 then allowedActions[ #allowedActions + 1] = "RebuildSimpleRange" allowedActions[ #allowedActions + 1] = "RebuildBitspawnLowCI" end AddRegionBanderActionsToList( allowedActions, idx ) -- push, move, flex, helix, sheet if PuzzleHasContactMap then allowedActions[ #allowedActions + 1] = "ContactMapper" allowedActions[ #allowedActions + 1] = "SomeContactMapBands" allowedActions[ #allowedActions + 1] = "ContactMapTwoRegions" if #NonLockedSegList > 0 then allowedActions[ #allowedActions + 1] = "ContactMapRebuildRange" end if SegHasContactData( idx, CONTACTMAP_THRESHOLD ) then allowedActions[ #allowedActions + 1] = "PushPullContactMap" allowedActions[ #allowedActions + 1] = "VoidCrusherContactMap" allowedActions[ #allowedActions + 1] = "ContactMapForSeg" end end return PickActionFromList( allowedActions, idx ) end function PickLateAction( idx ) if idx == nil or idx == 0 then idx = randomMovableSeg( ) end local allowedActions = { "CentralBander", "EveryXToY", "PushPull", "VoidCrusher", "BIS", "BandBetween", "IdealizePoint", } if #NonLockedSegList > 0 then allowedActions[ #allowedActions + 1] = "RebuildInRange" allowedActions[ #allowedActions + 1] = "IdealizeInRange" allowedActions[ #allowedActions + 1] = "RebuildSimplePoint" allowedActions[ #allowedActions + 1] = "RebuildSimple2Point" end AddRegionBanderActionsToList( allowedActions, idx ) if PuzzleHasContactMap and SegHasContactData( idx, CONTACTMAP_THRESHOLD ) then allowedActions[ #allowedActions + 1] = "ContactTwoSegs" allowedActions[ #allowedActions + 1] = "ContactMapTwoRegions" allowedActions[ #allowedActions + 1] = "ContactMapRebuildRange" allowedActions[ #allowedActions + 1] = "ContactMapRebuildSS" if SegHasContactData( idx, CONTACTMAP_THRESHOLD ) then allowedActions[ #allowedActions + 1] = "ContactMapForSeg" end end return PickActionFromList( allowedActions, idx ) end function PickContactMapAction( 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 ---------------------------------------------------------------- -- SA's Neighbor generator ---------------------------------------------------------------- function CreateNeighborPose( 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_NeighborTempSA ) local goodNeighbor = false while not goodNeighbor do local oldTime = os.time( ) local changeSucceeded = false goodNeighbor = false save.Quickload( QS_NeighborTempSA ) 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 if rebuild_fails > MAX_REBUILD_FAILS then -- sometimes there are no successful ways to do a rebuild, we must bail rebuild_fails = 0 return false end end -- END while not goodNeighbor -- Look over the result local newScore = getScore( ) local newCreditBest = creditbest.GetScore( ) if newCreditBest ~= oldCreditBest and newCreditBest ~= newScore then -- we missed a transient -- trying to regain it can be problematic for some wigglers -- (eg WiggleZlbNicely) because the creditbest pose may have bands. print( "!!! CreditBest changed: old="..oldCreditBest.." new="..newCreditBest ) end DebugPrint( "Neighbor Score: " .. newScore) return true end function DoFuseWiggle( ) oldScore = getQuickScore( ) oldTime = os.time( ) local r = random( ) if r < PROB_FUSEWIGGLE_LOCAL then print( "IterativeWiggleLocalByChunk" ) algName = "IterativeWiggleLocalByChunk" IterativeWiggleLocalByChunk( 1, segCt, FUSEWIGGLE_GAIN, random( 3, 6 ), false ) elseif r < PROB_FUSEWIGGLE_LOCAL + PROB_FUSEWIGGLE_BLUEFUSE then print( "BlueFuse" ) algName = "BlueFuse" BlueFuse( ) else -- PROB_FUSEWIGGLE_CUTFUSE print( "IterativeCutAndWiggle") algName = "IterativeCutAndWiggle" IterativeCutAndWiggle( FUSEWIGGLE_ITERS, FUSEWIGGLE_GAIN, random( 3,11 ) ) for i=1, segCt do -- deliberate non-use of ResetCuts DeleteCutAtIndex( i ) end end local deltaScore = getQuickScore( ) - oldScore local deltaTime = os.time( ) - oldTime AddActionToStats( deltaScore, deltaTime ) 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 early functions. -- Later I prefer Middling or Zero. -- Note that "num" goes from 0 to 1 over the life of the SA function. function T_OneOverX( num ) 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 -- always accept increased scores if T == 0.0 then return 0.0 end -- always reject decreased scores if temp = 0 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, failNoGainPt, NeighborGenerationFunction, TempFunc, qs_best, abbr ) -- emax is expected to "never happen" if TempFunc == nil then TempFunc = T_OneOverX end local estart = E( ) local ecurr = estart save.Quicksave( QS_TopLevelTempSA ) -- a private slot local ebest = ecurr local startTime = os.time( ) local k = 0.0 while k < kmax and ecurr > emax do save.Quickload( QS_TopLevelTempSA ) local T = TempFunc( k / kmax ) print("#" ..abbr .. k+1 .. " tp=" .. TrimNum( T ).. " sc=" .. TrimNum( getScore( ) ).. " time=" .. os.time( )-startTime ) local succeeded = CreateNeighborPose( 0.1, NeighborGenerationFunction ) if not succeeded then return k end local enew = E( ) local p = P(ecurr, enew, T) local r = random( ) if p > r or enew < ecurr then if enew < ecurr then print( "++++ accepting state: score " ..TrimNum( getScore())) if enew < ebest then ebest = enew SaveBest( false, qs_best ) end elseif enew > ecurr then print( "++++ overriding state: score " ..TrimNum( getScore())) else print( "++++ same-score state: score " ..TrimNum( getScore())) end save.Quicksave( QS_TopLevelTempSA ) ecurr = enew end if k + 1 >= failNoGainPt and k + 1 < kmax and ebest >= estart then print( "---- Reached step "..(k+1).." with no improvement - bailing" ) LoadBest( qs_best ) return k+1 -- give up: we've not achieved any improvement and we think we never will end k = k + 1.0 end LoadBest( qs_best ) return k end function PerformSA( failNoGainPt, ctRestarts, stepsPerRun, minGainPerRun, NeighborGenerationFunction, TempFunc, quitIfTooManyFails, qs_best, abbr ) 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( false, qs_best ) if abbr == nil then abbr = "SA" else abbr = abbr .. "-SA" end if ctRestarts > 1 then abbr = abbr .. ctRestarts .. "." end abbr = abbr .. stepsPerRun .. ":" local ctFailedRuns = 0 local failPt = stepsPerRun if quitIfTooManyFails then failPt = failNoGainPt else failPt = stepsPerRun end for i=1, ctRestarts do LoadBest( qs_best ) local saRunStartScore = getScore( ) local saRunStartTime = os.time( ) print( "START: SA score=" ..TrimNum( saRunStartScore ).. " time=" ..saRunStartTime - saInitialTime ) abbrOut = abbr if ctRestarts > 1 then abbrOut = abbrOut .. i .. "." end stepCount = stepCount + SA( stepsPerRun, -999999.0, failPt, NeighborGenerationFunction, TempFunc, qs_best, abbrOut ) LoadBest( qs_best ) newscore = getScore( ) gain = newscore - bestScore if newscore > bestScore then bestScore = newscore end print( "FINISH: SA score=" ..TrimNum( bestScore ).. " gain=" ..TrimNum( gain ).. " time=" ..os.time( ) - saRunStartTime ) if quitIfTooManyFails and gain < minGainPerRun then -- this run wasn't profitable... ctFailedRuns = ctFailedRuns + 1 if ctFailedRuns > 3 and i < ctFailedRuns + 2 then -- ok, it was fun while it lasted, but this is getting silly print( "Too many failed runs - bailing" ) break end end end LoadBest( qs_best ) if ctRestarts > 1 then print( "FINAL SA: steps=" ..stepCount.. " gain=" ..TrimNum( getScore( ) - saInitialScore).. " time=" ..os.time( ) - saInitialTime ) end return stepCount end ---------------------------------------------------------------- -- SA OPERATION PERFORMING FUNCTIONS ---------------------------------------------------------------- function PerformEarlyOperation() -- no expander at first, some later. never fuses, always short runs -- no expander at first is to go quicker. EXPAND_BEFORE_ACTION = false USE_FUSEWIGGLE = false stepsEarly = 0 -- EARLY without expander EXPAND_BEFORE_ACTION = false tempFunc = T_OneOverX runcount = SA_NUMSTARTS_EARLY stepcount = SA_STEPSPERRUN_EARLY print( "SA-Early for " .. stepcount .. " steps and " .. runcount .. " runs; do not fail out" ) stepsEarly = PerformSA( stepcount, runcount, stepcount, SA_MINGAIN, PickEarlyAction, tempFunc, false, QS_Best, "EA1" ) -- EARLY with expander EXPAND_BEFORE_ACTION = ALLOW_EXPAND_FOR_ACTION PROB_USE_EXPANDER = PROB_USE_EXPANDER_EARLY2 tempFunc = T_MiddlingX -- T_OneOverX T_MiddlingX runcount = SA_NUMSTARTS_EARLY2 stepcount = SA_STEPSPERRUN_EARLY2 print( "SA-Early for " .. stepcount .. " steps and " .. runcount .. " runs; do not fail out" ) stepsEarly = stepsEarly + PerformSA( stepcount, runcount, stepcount, SA_MINGAIN, PickEarlyAction, tempFunc, false, QS_Best, "EA2" ) return stepsEarly end function PerformRebuildOperation( ) -- no fuses, some expander, short runs, 2nd cool if #NonLockedSegList == 0 then print( "Cannot do rebuild with no movable segments" ) return 0 end EXPAND_BEFORE_ACTION = false USE_FUSEWIGGLE = false stepsRebuild = 0 -- REBUILD with expander EXPAND_BEFORE_ACTION = ALLOW_EXPAND_FOR_ACTION PROB_USE_EXPANDER = PROB_USE_EXPANDER_REBUILD tempFunc = T_OneOverX runcount = SA_NUMSTARTS_REBUILD numSteps = SA_STEPSPERRUN_REBUILD print( "SARebuild-1/x for " .. numSteps .. " steps and " .. runcount .. " runs; do not fail out" ) stepsRebuild = PerformSA( numSteps, runcount, numSteps, SA_MINGAIN, PickRebuildAction, tempFunc, false, QS_Best, "RB1" ) -- REBUILD with expander and a cooler temperature EXPAND_BEFORE_ACTION = ALLOW_EXPAND_FOR_ACTION PROB_USE_EXPANDER = PROB_USE_EXPANDER_REBUILD tempFunc = T_MiddlingX runcount = SA_NUMSTARTS_REBUILD2 numSteps = SA_STEPSPERRUN_REBUILD2 print( "SARebuild-Middling for " .. numSteps .. " steps and " .. runcount .. " runs; do not fail out" ) stepsRebuild = stepsRebuild + PerformSA( numSteps, runcount, numSteps, SA_MINGAIN, PickRebuildAction, tempFunc, false, QS_Best, "RB2" ) return stepsRebuild end function PerformMidgameOperation() -- no fuses, yes expander, be grateful for anything we get. EXPAND_BEFORE_ACTION = ALLOW_EXPAND_FOR_ACTION PROB_USE_EXPANDER = PROB_USE_EXPANDER_MID1 print( "SA-Midgame1 for " .. SA_STEPSPERRUN_MID .. " steps and " .. SA_NUMSTARTS_MID .. " runs; do not fail out" ) stepCt = PerformSA( SA_STEPSPERRUN_MID, SA_NUMSTARTS_MID, SA_STEPSPERRUN_MID, SA_MINGAIN, PickMidgameAction, T_Zero, false, QS_Best, "MID1" ) PROB_USE_EXPANDER = PROB_USE_EXPANDER_MID2 print( "SA-Midgame2 for " .. SA_STEPSPERRUN_MID2 .. " steps and " .. SA_NUMSTARTS_MID2 .. " runs; do not fail out" ) stepCt = stepCt + PerformSA( SA_STEPSPERRUN_MID2, SA_NUMSTARTS_MID2, SA_STEPSPERRUN_MID2, SA_MINGAIN, PickMidgameAction, T_Zero, false, QS_Best, "MID2" ) return stepCt end function PerformLateOperation( ) -- yes fuses, yes expander, be grateful for anything we get. EXPAND_BEFORE_ACTION = ALLOW_EXPAND_FOR_ACTION PROB_USE_EXPANDER = PROB_USE_EXPANDER_LATE print( "SA-Late for " .. SA_STEPSPERRUN_LATE .. " steps and " .. SA_NUMSTARTS_LATE .. " runs; do not fail out" ) local qwg = QUICKWIGGLE_GAIN local swg = FUSEWIGGLE_GAIN QUICKWIGGLE_GAIN = 0.1 FUSEWIGGLE_GAIN = 0.01 stepCt = PerformSA( SA_STEPSPERRUN_LATE, SA_NUMSTARTS_LATE, SA_STEPSPERRUN_LATE, SA_MINGAIN, PickLateAction, T_Zero, false, QS_Best, "LATE"..SA_STEPSPERRUN_LATE ) QUICKWIGGLE_GAIN = qwg FUSEWIGGLE_GAIN = swg return stepCt end function PerformLowCIOperation( ) -- Does not seem worth the time it takes for "stable" poses -- pre-expander, no fuses, CI<=0.5 savCI = GetCI( ) EXPAND_BEFORE_ACTION = ALLOW_EXPAND_FOR_ACTION USE_LOWCI_WIGGLES = true ExactSetCI( 0.20 ) SCALE_MAXCI = 0.20 print( "SA-LowCI20 for " .. SA_STEPSPERRUN_LOW20 .. " steps and " .. SA_NUMSTARTS_LOW20 .. " runs; fail if no gain by " .. SA_FAILIFNOGAIN ) stepCt = PerformSA( SA_FAILIFNOGAIN, SA_NUMSTARTS_LOW20, SA_STEPSPERRUN_LOW20, SA_MINGAIN, PickLowCIAction, T_OneOverX, true, QS_Best, "20CI"..SA_STEPSPERRUN_LOW20 ) ExactSetCI( 0.50 ) SCALE_MAXCI = 0.50 print( "SA-LowCI50 for " .. SA_STEPSPERRUN_LOW50 .. " steps and " .. SA_NUMSTARTS_LOW50 .. " runs; fail if no gain by " .. SA_FAILIFNOGAIN ) stepCt = stepCt + PerformSA( SA_FAILIFNOGAIN, SA_NUMSTARTS_LOW50, SA_STEPSPERRUN_LOW50, SA_MINGAIN, PickLowCIAction, T_OneOverX, true, QS_Best, "50CI"..SA_STEPSPERRUN_LOW50 ) USE_LOWCI_WIGGLES = false ExactSetCI( savCI ) SCALE_MAXCI = savCI return stepCt end function PerformContactMapOperation( ) -- not in basic set, must have been specifically asked for print( "SA-Contact for " .. SA_STEPSPERRUN_CMAP .. " steps and " .. SA_NUMSTARTS_CMAP .. " runs; fail if no gain by " .. SA_FAILIFNOGAIN ) stepCt = PerformSA( SA_FAILIFNOGAIN, SA_NUMSTARTS_CMAP, SA_STEPSPERRUN_CMAP, SA_MINGAIN, PickContactMapAction, T_OneOverX, true, QS_Best, "CT"..SA_STEPSPERRUN_CMAP ) return stepCt end function PerformOperation( OperationChosen ) local moInitialScore = getScore( ) local moInitialTime = os.time( ) local numSteps = 0 local failNoGain = 0 local stepCt = 0 rebuild_fails = 0 -- global that gets incremented when a rebuild fails SaveBest( ) if rebuild_fails >= MAX_REBUILD_FAILS then print( "rebuild fail count exceeded in PerformOperation") rebuild_fails = 0 return end print( "Performing ".. OperationChosen ) if OperationChosen == "SASequence" then PerformSASequence( ) elseif OperationChosen == "SAEarly" then stepCt = PerformEarlyOperation( ) print( "SA-Early finished" ) elseif OperationChosen == "SARebuild" then stepCt = PerformRebuildOperation( ) print( "SARebuild finished" ) elseif OperationChosen == "SAMidgame" then stepCt = PerformMidgameOperation() print( "SA-Midgame finished" ) elseif OperationChosen == "SALowCI" then stepCt = PerformLowCIOperation( ) print( "SA-LowCI finished" ) elseif OperationChosen == "SALate" then stepCt = PerformLateOperation( ) print( "SA-Late finished" ) elseif OperationChosen == "SAContact" then stepCt = PerformContactMapOperation( ) print( "SA-Contact finished" ) else print( "!! Unknown operation" ) end LoadBest( ) local endScore = getScore( ) return stepCt end function PerformSASequence( ) SaveBest( ) print( "Begin SA Sequence" ) scoreInit = getScore( ) timeInit = os.time( ) -- INIT WSWSW timeStart = os.time() EnableAndDisableCysteineBands( ) LongWiggle( ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) LongWiggle( ) ShakeOrMutate( SHAKESIDECHAIN_ITERS ) LongWiggle( ) scoreWswsw = getScore( ) timeWswsw = os.time( ) - timeStart DelBands() SaveBest() -- EARLY timeStart = os.time() stepsEarly = PerformEarlyOperation( ) timeEarly = os.time() - timeStart scoreEarly = getScore( ) save.Quicksave( 4 ) print( "SASEQ-Early finished - stored in qs 4" ) -- REBUILDS timeStart = os.time() stepsRebuild = PerformRebuildOperation( ) timeRebuild = os.time() - timeStart scoreRebuild = getScore( ) save.Quicksave( 5 ) print( "SARebuild finished - stored in qs 5" ) -- MIDGAME timeStart = os.time() stepsMid = PerformMidgameOperation( ) timeMid = os.time( ) - timeStart scoreMid = getScore( ) print( "SAMidgame finished" ) -- FINAL -- I question caller's wisdom, but do as they asked... if ALLOW_FUSE then save.Quicksave( 6 ) print( "SAMidgame result stored in qs 6" ) USE_FUSEWIGGLE = true IterativeCutAndWiggle( FUSEWIGGLE_ITERS, FUSEWIGGLE_GAIN, random( 3,11 )) timeStart = os.time() stepsLate = PerformLateOperation( ) timeLate = os.time( ) - timeStart scoreLate = getScore( ) end print( "End SA Sequence: gain="..getScore( ) - scoreInit.. " time="..os.time( ) - timeInit ) print( " Stage Time Steps Score" ) print( " Init: " ..PadNumString(0,6).. " " ..PadNumString(0,5).. " " .. TrimNum(scoreInit)) print( " WSWSW: " ..PadNumString(timeWswsw,6).. " " ..PadNumString(0,5).. " " .. TrimNum(scoreWswsw)) print( " Early: " ..PadNumString(timeEarly,6).. " " ..PadNumString(stepsEarly,5).. " " .. TrimNum(scoreEarly)) print( " Rebuild: " ..PadNumString(timeRebuild,6).." " ..PadNumString(stepsRebuild,5).. " " .. TrimNum(scoreRebuild)) print( " Midgame: " ..PadNumString(timeMid,6).. " " ..PadNumString(stepsMid,5).. " " .. TrimNum(scoreMid)) if ALLOW_FUSE then print( " Late: " ..PadNumString(timeLate,6).. " " ..PadNumString(stepsLate,5).. " " .. TrimNum(scoreLate)) end end ----------------------------------------------------------------------------- -- DIALOG BOX SEQUENCE FROM THE NETHERWORLD ----------------------------------------------------------------------------- function PutSlowWiggleOptionsInBox( dlg ) dlg.sepSWSA = dialog.AddLabel( "----------------Fuse Wiggles------------------------" ) dlg.swgain = dialog.AddSlider( "Fuse-WiggleGain", FUSEWIGGLE_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_FUSEWIGGLE_LOCAL, 0, 1, 2 ) -- slow, poor dlg.wbfuse= dialog.AddSlider( "BlueFuse", PROB_FUSEWIGGLE_BLUEFUSE, 0, 1, 2 ) -- less slow, moderately good dlg.wcwfuse= dialog.AddSlider( "C&WFuse", PROB_FUSEWIGGLE_CUTFUSE, 0, 1, 2 ) -- slow, moderately good end function SetSlowWiggleOptions( dlg ) FUSEWIGGLE_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 these don't sum to 1: they'll be scaled t2 = .25 t3 = .25 end PROB_FUSEWIGGLE_LOCAL = t1 / ( t1 + t2 + t3 ) PROB_FUSEWIGGLE_BLUEFUSE = t2 / ( t1 + t2 + t3 ) PROB_FUSEWIGGLE_CUTFUSE = t3 / ( t1 + t2 + t3 ) DebugPrint( "SlowGain="..FUSEWIGGLE_GAIN ) DebugPrint( "Prob_slowwiggle-local="..PROB_FUSEWIGGLE_LOCAL ) DebugPrint( "Prob_slowwiggle-bluefuse="..PROB_FUSEWIGGLE_BLUEFUSE ) DebugPrint( "Prob_slowwiggle-cutfuse="..PROB_FUSEWIGGLE_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.bdwigtargdelta = dialog.AddSlider( "BWig ScoreDelta", BANDEDWIGGLE_TARGET, 50, 1000, 0 ) dlg.bdwigminexpand = dialog.AddSlider( "MinExpand", BAND_MINEXPAND, 1.00, 1.10, 2 ) dlg.bdwigmaxexpand = dialog.AddSlider( "MaxExpand", BAND_MAXEXPAND, 1.10, 3.00, 2 ) dlg.bdwigmincompr = dialog.AddSlider( "MinCompress", BAND_MINCOMPRESS, 0.85, 1.00, 2 ) dlg.bdwigmaxcompr = dialog.AddSlider( "MaxCompress", BAND_MAXCOMPRESS, 0.25, 0.85, 2 ) end function SetBanderOptions( dlg ) COREPULLING_CI = dlg.bdcpci.value BAND_STRENGTH_REGION = dlg.bdregstren.value BANDEDWIGGLE_TARGET = dlg.bdwigtargdelta.value BAND_MINCOMPRESS = dlg.bdwigmincompr.value BAND_MAXCOMPRESS = dlg.bdwigmaxcompr.value BAND_MINEXPAND = dlg.bdwigminexpand.value BAND_MAXEXPAND = dlg.bdwigmaxexpand.value DebugPrint("Core-pulling CI="..COREPULLING_CI ) DebugPrint("Region-bander strength="..BAND_STRENGTH_REGION ) DebugPrint("Banded Wiggler Target Delta="..BANDEDWIGGLE_TARGET ) DebugPrint("Max Compress="..BAND_MINCOMPRESS) DebugPrint("Min Compress="..BAND_MAXCOMPRESS) DebugPrint("Min Expand="..BAND_MINEXPAND ) 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 PutLowCI20OptionsInBox( dlg ) dlg.stepsperLCI20 = dialog.AddSlider( "Steps20CI", SA_STEPSPERRUN_LOW20, 10, 200, 0 ) dlg.startsLCI20 = dialog.AddSlider( "Runs20CI", SA_NUMSTARTS_LOW20, 1, 20, 0 ) end function SetLowCI20Options( dlg ) SA_STEPSPERRUN_LOW20 = dlg.stepsperLCI20.value SA_NUMSTARTS_LOW20 = dlg.startsLCI20.value DebugPrint("LowCI steps,starts="..SA_STEPSPERRUN_LOW20..","..SA_NUMSTARTS_LOW20 ) end function PutLowCI50OptionsInBox( dlg ) dlg.stepsperLCI50 = dialog.AddSlider( "Steps50CI", SA_STEPSPERRUN_LOW50, 10, 200, 0 ) dlg.startsLCI50 = dialog.AddSlider( "Runs50CI", SA_NUMSTARTS_LOW50, 1, 20, 0 ) end function SetLowCI50Options( dlg ) SA_STEPSPERRUN_LOW50 = dlg.stepsperLCI50.value SA_NUMSTARTS_LOW50 = dlg.startsLCI50.value DebugPrint("LowCI steps,starts="..SA_STEPSPERRUN_LOW50..","..SA_NUMSTARTS_LOW50 ) end function PutEarlyOptionsInBox( dlg ) dlg.stepsperEa = dialog.AddSlider( "StepsEarly1", SA_STEPSPERRUN_EARLY, 10, 50, 0 ) dlg.startsEa = dialog.AddSlider( "RunsEarly1", SA_NUMSTARTS_EARLY, 1, 300, 0 ) dlg.stepsperEa2 = dialog.AddSlider( "StepsEarly2", SA_STEPSPERRUN_EARLY2, 10, 50, 0 ) dlg.startsEa2 = dialog.AddSlider( "RunsEarly2", SA_NUMSTARTS_EARLY2, 1, 300, 0 ) dlg.probExpandEa2 = dialog.AddSlider("ProbExpanderE2", PROB_USE_EXPANDER_EARLY2, 0, 1.0, 2 ) end function SetEarlyOptions( dlg ) SA_STEPSPERRUN_EARLY = dlg.stepsperEa.value SA_NUMSTARTS_EARLY = dlg.startsEa.value SA_STEPSPERRUN_EARLY2 = dlg.stepsperEa2.value SA_NUMSTARTS_EARLY2 = dlg.startsEa2.value PROB_USE_EXPANDER_EARLY2 = dlg.probExpandEa2.value DebugPrint("Early steps,starts="..SA_STEPSPERRUN_EARLY..","..SA_NUMSTARTS_EARLY ) DebugPrint("Early2 steps,starts="..SA_STEPSPERRUN_EARLY2..","..SA_NUMSTARTS_EARLY2 ) DebugPrint("Prob Early Expander = " .. PROB_USE_EXPANDER_EARLY2) end function PutRebuildOptionsInBox( dlg ) dlg.stepsperRb = dialog.AddSlider( "StepsRebuild1", SA_STEPSPERRUN_REBUILD, 10, 50, 0 ) dlg.startsRb = dialog.AddSlider( "RunsRebuild1", SA_NUMSTARTS_REBUILD, 1, 300, 0 ) dlg.stepsperRb2 = dialog.AddSlider( "StepsRebuild2", SA_STEPSPERRUN_REBUILD2, 10, 50, 0 ) dlg.startsRb2 = dialog.AddSlider( "RunsRebuild2", SA_NUMSTARTS_REBUILD2, 1, 300, 0 ) dlg.probExpandRb = dialog.AddSlider("ProbExpanderR", PROB_USE_EXPANDER_REBUILD, 0, 1.0, 2 ) end function SetRebuildOptions( dlg ) SA_STEPSPERRUN_REBUILD = dlg.stepsperRb.value SA_NUMSTARTS_REBUILD = dlg.startsRb.value SA_STEPSPERRUN_REBUILD2 = dlg.stepsperRb2.value SA_NUMSTARTS_REBUILD2 = dlg.startsRb2.value PROB_USE_EXPANDER_REBUILD = dlg.probExpandRb.value DebugPrint("Rebuild steps,starts="..SA_STEPSPERRUN_REBUILD..","..SA_NUMSTARTS_REBUILD ) DebugPrint("Rebuild2 steps,starts="..SA_STEPSPERRUN_REBUILD2..","..SA_NUMSTARTS_REBUILD2 ) DebugPrint("Prob Rebuild Expander = " .. PROB_USE_EXPANDER_REBUILD) end function PutMidOptionsInBox( dlg ) dlg.stepsperMv = dialog.AddSlider( "StepsMid1", SA_STEPSPERRUN_MID, 100, 500, 0 ) dlg.startsMv = dialog.AddSlider( "RunsMid1", SA_NUMSTARTS_MID, 1, 5, 0 ) dlg.probExpandMv = dialog.AddSlider("ProbExpanderM1", PROB_USE_EXPANDER_MID1, 0, 1.0, 2 ) dlg.stepsperMid = dialog.AddSlider("StepsMid2", SA_STEPSPERRUN_MID2, 100, 500, 0 ) dlg.startsMid = dialog.AddSlider( "RunsMid2", SA_NUMSTARTS_MID2, 1, 5, 0 ) dlg.probExpandMid = dialog.AddSlider("ProbExpanderM2", PROB_USE_EXPANDER_MID2, 0, 1.0, 2 ) dlg.probTarg = dialog.AddSlider( "Prob Mid2 Targeted", PROB_TARGETED, 0, 1.0, 2 ) end function SetMidOptions( dlg ) SA_STEPSPERRUN_MID2 = dlg.stepsperMid.value SA_NUMSTARTS_MID2 = dlg.startsMid.value PROB_USE_EXPANDER_MID2 = dlg.probExpandMid.value SA_STEPSPERRUN_MID = dlg.stepsperMv.value SA_NUMSTARTS_MID = dlg.startsMv.value PROB_USE_EXPANDER_MID1 = dlg.probExpandRb.value PROB_TARGETED = dlg.probTarg.value DebugPrint("Mid1 steps,starts="..SA_STEPSPERRUN_MID..","..SA_NUMSTARTS_MID ) DebugPrint("Prob Mid1 Expander = " .. PROB_USE_EXPANDER_MID1) DebugPrint("Mid2 steps,starts="..SA_STEPSPERRUN_MID2..","..SA_NUMSTARTS_MID2 ) DebugPrint("Prob Mid2 Expander = " .. PROB_USE_EXPANDER_MID2) DebugPrint("Prob Mid2 uses targeted operations = " .. PROB_TARGETED) end function PutLateOptionsInBox( dlg ) dlg.stepsperLt = dialog.AddSlider( "StepsLate", SA_STEPSPERRUN_LATE, 10, 500, 0 ) dlg.startsLt = dialog.AddSlider( "RunsLate", SA_NUMSTARTS_LATE, 1, 10, 0 ) dlg.probExpandLt = dialog.AddSlider("ProbExpander", PROB_USE_EXPANDER_LATE, 0, 1.0, 2 ) end function SetLateOptions( dlg ) if ALLOW_FUSE then SA_STEPSPERRUN_LATE = dlg.stepsperLt.value SA_NUMSTARTS_LATE = dlg.startsLt.value PROB_USE_EXPANDER_LATE = dlg.probExpandLt.value DebugPrint("Late steps,starts="..SA_STEPSPERRUN_LATE..","..SA_NUMSTARTS_LATE ) DebugPrint("Prob Late Expander = " .. PROB_USE_EXPANDER_LATE) end end function PutContactMapOptionsInBox( dlg ) dlg.stepsperCMap = dialog.AddSlider( "StepsCMap", SA_STEPSPERRUN_CMAP, 10, 200, 0 ) dlg.startsCMap = dialog.AddSlider( "RunsLowCMap", SA_NUMSTARTS_CMAP, 1, 20, 0 ) end function SetContactMapOptions( dlg ) SA_STEPSPERRUN_CMAP = dlg.stepsperCMap.value SA_NUMSTARTS_CMAP = dlg.startsCMap.value DebugPrint("CMap steps,starts="..SA_STEPSPERRUN_CMAP..","..SA_NUMSTARTS_CMAP ) end function PutGainOptionsInBox( dlg ) dlg.samingain = dialog.AddSlider( "MinGainForRuns", SA_MINGAIN, 0, 5, 1 ) dlg.quitnogain = dialog.AddSlider( "StopIfNoGain", SA_FAILIFNOGAIN, 1, 200, 0 ) end function SetGainOptions( dlg ) SA_MINGAIN = dlg.samingain.value SA_FAILIFNOGAIN = dlg.quitnogain.value DebugPrint("Step to stop at if no gain="..SA_FAILIFNOGAIN ) DebugPrint("SA halt if gain < "..SA_MINGAIN) end function PutSASequenceOptionsInBox( dlg ) PutEarlyOptionsInBox( dlg ) PutRebuildOptionsInBox( dlg ) PutMidOptionsInBox( dlg ) dlg.wantFuses = dialog.AddCheckbox( "Add final fuse run", false ) PutLateOptionsInBox( dlg ) PutGainOptionsInBox( dlg ) end function SetSASequenceOptions( dlg ) SetEarlyOptions( dlg ) SetRebuildOptions( dlg ) SetMidOptions( dlg ) ALLOW_FUSE = dlg.wantFuses.value SetLateOptions( dlg ) SetGainOptions( dlg ) end function AddBasicSpecialOptions( dlg, includeBander, allowFuseWiggle ) dlg.cmtSO = dialog.AddLabel( "----- Advanced SA Parameters -----" ) dlg.IdRAdvancedPars= dialog.AddCheckbox( "Idealize/Rebuild", false ) if allowFuseWiggle then dlg.WigglerPars= dialog.AddCheckbox( "Fuser", false ) end if includeBander then dlg.BanderPars= dialog.AddCheckbox( "Bander", false ) end dlg.maxCI = dialog.AddSlider( "MaxCI", SCALE_MAXCI, 0, 1, 2 ) dlg.ok = dialog.AddButton( "OK", 1 ) dlg.cancel = dialog.AddButton( "Cancel", 0 ) end function SetBasicSpecialOptions( dlg, includeBander, allowFuseWiggle ) if not dlg.IdRAdvancedPars.value and not ((not forbidFuseWiggle) and dlg.WigglerPars.value) and not (includeBander and dlg.BanderPars.value) then return end SCALE_MAXCI = dlg.maxCI.value DebugPrint( "Using MaxCI="..SCALE_MAXCI ) dlg2 = dialog.CreateDialog( "Special" ) if dlg.IdRAdvancedPars.value then PutIdealizeOptionsInBox( dlg2 ) PutGeneralRebuildOptionsInBox( dlg2 ) end if allowFuseWiggle and dlg.WigglerPars.value then PutSlowWiggleOptionsInBox( dlg2 ) end if includeBander and dlg.BanderPars.value then PutBanderOptionsInBox( dlg2 ) end dlg2.ok = dialog.AddButton( "OK", 1 ) dlg2.cancel = dialog.AddButton( "Cancel", 0 ) if dialog.Show( dlg2 ) == 0 then return false end if dlg.IdRAdvancedPars.value then SetIdealizeOptions( dlg2 ) SetGeneralRebuildOptions( dlg2 ) end if allowFuseWiggle and dlg.WigglerPars.value then SetSlowWiggleOptions( dlg2 ) end if includeBander and dlg.BanderPars.value then SetBanderOptions( dlg2 ) end end function PutCysteineOptionsInBox( dlg ) str = "" for i=1, #cysteineSegs do str = str..cysteineSegs[i] if i < #cysteineSegs then str = str.. "," end end dlg.cmtCy = dialog.AddLabel( "Cysteines: "..str ) str = "" for i=1, #cysteinePairs do pair = cysteinePairs[i] str = str..pair[1]..","..pair[2] if i < #cysteinePairs then str = str .. "; " end end dlg.cmtCy2 = dialog.AddLabel( "Guessed pairs: "..str ) dlg.cyPairsWanted = dialog.AddSlider( "Pair count", #cysteinePairs, 0, #cysteinePairs, 0 ) dlg.cyPairings = dialog.AddTextbox( "Pairings", str) if InitialBandCount > 0 then dlg.cmtCy3 = dialog.AddLabel( "Cysteine-bander cannot honor user bands" ) end dlg.ok = dialog.AddButton( "OK", 1 ) dlg.cancel = dialog.AddButton( "Cancel", 0 ) end function parseCysteineStr( ctPairsWanted, str ) local cysList = {} -- v1, v2: '(%d+),(%d+)[; ]+' for v1 in string.gfind(str, '-?(%d+)') do v1 = tonumber( v1 ) if v1 < 1 or v1 > segCt then return false end if structure.GetAminoAcid(v1) ~= 'c' then return false end cysList[#cysList+1] = v1 end if #cysList ~= 2*ctPairsWanted then return false end for i = 1, #cysList-1 do val = cysList[i] for j=i+1, #cysList do if cysList[j+1] == val then return false end end end -- ok, we believe the list is a correct number of distinct cysteines cysteinePairs = {} for i=1, ctPairsWanted do cysteinePairs[#cysteinePairs+1] = {cysList[2*i-1], cysList[2*i]} end end function SetCysteineOptions( dlg ) ctCysPairs = dlg.cyPairsWanted.value if dlg.cyPairings.value ~= nil then parseCysteineStr( ctCysPairs, dlg.cyPairings.value) else -- this happens if user puts empty comment, -- but says they want a number of pairs smaller than expected -- we interpret this as request to truncate accepted list while ctCysPairs < #cysteinePairs do table.remove(cysteinePairs, #cysteinePairs) end end if ctCysPairs > 0 then str = "" for i=1, #cysteinePairs do pair = cysteinePairs[i] str = str..pair[1]..","..pair[2] if i < #cysteinePairs then str = str .. "; " end end DebugPrint( "CysteinePairs: "..str ) print( "Try to add "..ctCysPairs.." cysteine bands" ) else print( "No cysteine bands wanted, none made" ) end end function LetUserChooseParams( ) dlg = dialog.CreateDialog( "Simulated Annealing And More" ) dlg.cmt1 = dialog.AddLabel( "Pick one of following (topmost checked will be run)" ) dlg.wantSASeq = dialog.AddCheckbox( "SA - Sequence", true ) dlg.cmt2 = dialog.AddLabel( "-- Single Options" ) dlg.wantSAEarly = dialog.AddCheckbox( "SA - Early", false ) dlg.wantSaRebuilder = dialog.AddCheckbox( "SA - Rebuild", false ) dlg.wantSAMid = dialog.AddCheckbox( "SA - Midgame", false ) dlg.cmt3 = dialog.AddLabel( "-- Oddball Single Options" ) dlg.wantSALowCI = dialog.AddCheckbox( "SA - LowCI", false ) dlg.wantSALate = dialog.AddCheckbox( "SA - Late Game", false ) if PuzzleHasContactMap then dlg.wantSAContact = dialog.AddCheckbox( "SA - ContactMap", false ) end dlg.modLabel = dialog.AddLabel( "------------------Option(s)-----------------------" ) if #cysteineSegs >= 2 then dlg.playCysteineGames = dialog.AddCheckbox( "Bond cysteine pairs", false ) end 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.allowExpanding = dialog.AddCheckbox( "Allow Expansion", true ) 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 -- PICK the Operation if dlg.wantSASeq.value then OperationChosen = "SASequence" elseif dlg.wantSALowCI.value then OperationChosen = "SALowCI" elseif dlg.wantSAEarly.value then OperationChosen = "SAEarly" elseif dlg.wantSaRebuilder.value then OperationChosen = "SARebuild" elseif dlg.wantSAMid.value then OperationChosen = "SAMidgame" elseif dlg.wantSALate.value then OperationChosen = "SALate" elseif PuzzleHasContactMap and dlg.wantSAContact.value then OperationChosen = "SAContact" else return false -- something went wrong... end -- PICK other info from initial dialog box 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 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_FUSEWIGGLE_CUTFUSE = 0.00 -- don't use wigglers that put cuts PROB_FUSEWIGGLE_LOCAL = 0.00 -- this one's too useless PROB_FUSEWIGGLE_BLUEFUSE = 1.0 end DebugPrint( "Preserve user cuts? ".. BoolStr( PRESERVE_USER_CUTS ) ) ALLOW_FUSE = false if OperationChosen == "SALate" then ALLOW_FUSE = true DebugPrint( "Final Fuse Run? true") end ALLOW_EXPAND_FOR_ACTION = dlg.allowExpanding.value DebugPrint( "Expand? ".. BoolStr( ALLOW_EXPAND_FOR_ACTION)) -- DO WE PLAY WITH CYSTEINES? if dlg.playCysteineGames.value == true then PERFORM_CYSTEINE_BANDING = true dlg2 = dialog.CreateDialog("Cysteine Pairings") PutCysteineOptionsInBox( dlg2 ) if dialog.Show( dlg2 ) ~= 0 then if InitialBandCount > 0 then print("Cannot honor user bands if cysteine bands are added") InitialBandCount = 0 DelBands( ) end SetCysteineOptions( dlg2 ) PutCysteinePairBands( ) end end -- ARE WE DONE? if not dlg.moreOptions.value then return true end -- NO: USER WANTS MORE OPTIONS, offer as relevant to OperationChosen if OperationChosen == "SASequence" then -- also allow user to turn on fuses here dlg = dialog.CreateDialog( "SA Sequence Parameters" ) PutSASequenceOptionsInBox( dlg ) AddBasicSpecialOptions( dlg, true, true ) if dialog.Show( dlg ) == 0 then return false end SetSASequenceOptions( dlg ) SetBasicSpecialOptions( dlg, true, true ) elseif OperationChosen == "SAEarly" then dlg = dialog.CreateDialog( "Early SA Options" ) PutEarlyOptionsInBox( dlg ) AddBasicSpecialOptions( dlg, true, false ) if dialog.Show( dlg ) == 0 then return false end SetEarlyOptions( dlg ) SetBasicSpecialOptions( dlg, true, false ) elseif OperationChosen == "SARebuild" then dlg = dialog.CreateDialog( "Rebuild-SA Parameters" ) PutRebuildOptionsInBox( dlg ) AddBasicSpecialOptions( dlg, false, false ) if dialog.Show( dlg ) == 0 then return false end SetRebuildOptions( dlg ) SetBasicSpecialOptions( dlg, false, false ) elseif OperationChosen == "SAMidgame" then dlg = dialog.CreateDialog( "Mid SA Options" ) PutMidOptionsInBox( dlg ) AddBasicSpecialOptions( dlg, true, false ) if dialog.Show( dlg ) == 0 then return false end SetMidOptions( dlg ) SetBasicSpecialOptions( dlg, true, false ) DebugPrint( "probTarg="..PROB_TARGETED ) elseif OperationChosen == "SALate" then ALLOW_FUSE = true dlg = dialog.CreateDialog( "Late SA Options" ) PutLateOptionsInBox( dlg ) AddBasicSpecialOptions( dlg, true, true ) if dialog.Show( dlg ) == 0 then return false end SetLateOptions( dlg ) SetBasicSpecialOptions( dlg, true, true ) elseif OperationChosen == "SAContact" then -- not in normal sequence, even for contact puzzles dlg = dialog.CreateDialog( "ContactMap SA Options" ) PutContactMapOptionsInBox( dlg ) AddBasicSpecialOptions( dlg, true, false ) if dialog.Show( dlg ) == 0 then return false end SetContactMapOptions( dlg ) SetBasicSpecialOptions( dlg, true, false ) elseif OperationChosen == "SALowCI" then -- not in normal sequence dlg = dialog.CreateDialog( "LowCI SA Options" ) PutLowCI20OptionsInBox( dlg ) PutLowCI50OptionsInBox( dlg ) AddBasicSpecialOptions( dlg, true, false ) if dialog.Show( dlg ) == 0 then return false end SetLowCI20Options( dlg ) SetLowCI50Options( dlg ) SetBasicSpecialOptions( dlg, true, false ) end return true end -------------------------------------------------------------------------- -- SETUP and CLEANUP -------------------------------------------------------------------------- function CleanPuzzleState( ) ExactSetCI( InitialClashImportance ) selection.DeselectAll( ) ResetFrozenness( ) ResetCuts( ) DelBands( true ) 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 SHOW_STATS_REPORT then print("") ReportStats( ) print( "TotalShakeTime="..TotalShakeTime ) -- because I want to know. end if string.find( errstr, "Cancelled" ) then print( "User cancel" ) else print( errstr ) end end PrintState( ) 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( ) 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 GenerateNonLockedSegList( ) StoreSecondaryStructure( ) SaveFrozenness( ) FindAllMovableSegs( ) LocateHelices( ) LocateSheets( ) LocateLoops( ) LocateMissingHelices( ) -- places where a possible helix may be hiding if #possibleHelices > 0 then print( "Treating some loops as helices: count of ranges ="..#possibleHelices ) end SetCysteineSegList( ) GuessCysteinePairs( ) DelBands( ) selection.DeselectAll( ) recentbest.Save( ) -- do this after DelBands end -------------------------------------------------------------------------- -- MAIN -------------------------------------------------------------------------- function main( ) OperationChosen = "none" InitializePuzzleState() didSomething = false print( "Start is in qs="..QS_Start.."; Best is in "..QS_Best ) if PuzzleHasContactMap then print( "CM-best is in "..QS_CmBest ) end ------------------------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 ) if OperationChosen == "SASequence" then print("End of Early - qs 4") print("End of Rebuild - qs 5") if ALLOW_FUSE then print("End of Midgame - qs 6") end end didSomething = true LoadBest( ) PrintState( ) if SHOW_STATS_REPORT then ReportStats() end ResetStats() end print( " Done!" ) if didSomething then End( ) end end xpcall( main, End )

Comments