Code
--GAB_proj_beta_v1.3.2
--[[
Genetic Algorithm on Bands by Rav3n_pl,CartoonVillan, Crashguard303.
additional code by jeff101, Bruno Kestemont,Timo van der Laan, LociOiling
]]--
-- GAB_proj_beta_v1.3.2
-- CodeBase: Rav3n_pl GAB 2.0.6, GAB_clean_cuts d005b
-- Timo filters,dialogs,some BrunoK mods,select sphere
--ToDo:
-- Mutate CI ? make it constant not scalable
--[[
Definitions:
band: randomised: start segment, end segment, length, strength
critter: set of bands
herd: set of critters
Overview:
- randomize needed bands
- randomly assiging them to critters
- score each critter
- keep critters scoring above score
- breed best critters
- breed bastards ( best one + random one )
- forget about rest of critters
- randomize new critters to fill herd
]]--
undo.SetUndo( false ) --2020 Bands are slow this supposedly speeds things along
-- ------------------------------------
-- ---------- GLOBAL VARS -------------
-- ------------------------------------
Recipe = "GAB_proj_beta"
Version = "v1.3.2"
ReVersion = Recipe .. " " .. Version
-- --
-- ------------- OPTIONS --------------
-- --
ShakMutRadius = 8 -- radius for select sphere
SmallMutate = false -- works for both shake and mutateOnce -selects sphere of segmnets
pullCI = 0.9 -- Clash Importance during pull
shakeCI = 0.21 -- CI when shake on qstab or mutate once
noQstab = true -- extra wiggle(0.4ci) and shake(1ci)
noFuze = true
fuzeThresh = 20 -- run fuze if we gain this many points or more
--qstabThresh = 1 -- run qstab if score drops more than x pts
onlyMutable = false -- if true use ONLY all mutable aas, overrides AlwaysUse - applies to ONE end of band only
mutateOnce = false -- if true do mutate( 1 ) after banded wiggle
mutateCI = 0.75 -- mutate on what clashing importance
mutateAlways = false -- if true use mutate( 1 ) instead of shake --no shake, no CI, if fuze then mutates everything x2 !!!
herd = --herd options
{
breedBest = 4, -- breed best 4 critters - all combinations = > 6 kids form 4 critters, 3 form 3, 1 form 2, 9 form 5 ;] -- ?????
keepBest = 2, -- save up to n best scoring critters, rest are forgotten
breedBastards = 8, -- number of best will have one random bastard
newRandom = 6, -- adding new random ones each generation
maxGen = 20, -- maximum generations
shuffle = true, -- set true to run critters in random order
renew = 2, -- create COMPLETELY NEW herd after that many gens w/o improvement
}
critter = --critter options
{
minBands = 3, -- minimum bands
maxBands = 7, -- maximum bands
keepScore = 0 , -- survive to next gen only if score higher than
breedScore = -20, -- will breed kids only if score higher. Bastards always breed
maxLoss = 30, -- maximum loss by critter. set 0 to disable -- hows this work ???
}
bands = --bands options
{
minStr = 0.3, -- minimum band str
maxStr = 1.1, -- maximum band str
minChng = 3, -- minimum change of band len accordig to current distance
maxUp = 6.1, -- maximum chnage up ( push )
maxDn = 6.9, -- maximum change down ( pull )
minSkip = 5, -- minimum segment distance between the from-to segments of a single band
minDist = 4, -- minimum spatial distance
minLen = 2, -- minimum length of created band
}
-- Any segs in DoNotUse will never be used . Ranges only --just patched this in; needs more elegance
-- example: 1,2,3,4 = "{1,4},"
DoNotUse = {--just comment lines below or add more areas to avoid
--{120, 134},
--{1, 50},
}
--It MAY be used !
AlwaysUse = { --areas should be always used --- applies to ONE end of band only
--{236,236}--ligand need to be at one end
--{308,311}, --loopy
--{272,319}, --loopy
}
-- at least one end of all bands, will always be from this list.
UseSegments = {} --use ONLY these segments for ONE end of a band, Comma Separated Values, NO ranges.
-- The only difference between AlwaysUse and UseSegments that i can see is one takes ranges the other takes csv.
-- They are both merged together later
-- bands by secondary structure
use =
{
Sheet = true, --set false to not band sheets
Helix = true, --set false to not band helices
Loop = true, --set false to not band loops
}
checkBoth = false --check both ends to above, if false only one end need to be true
--end user options
CapCI = true -- caps CI to userSetMaxCI value. false is relative CI
maxCI = true -- used to flag WF value
userSetMaxCI = 1 -- User set maximum CI value
WF = 1 -- WiggleFactor, user set more wiggles factor
band_Oligomer = true
HASMUTABLE = false
keep_bands = true
MINGAIN = 1
SYMMETRICBANDS = false
-- ------- END OPTIONS --------
-- -------- Shorts
BASESCORE = current.GetEnergyScore( ) -- unfiltered startscore for reference
Cut = structure.InsertCut
segCnt = structure.GetCount( )
USERBANDS = band.GetCount( )
-- ------- END GLOBALS --------
--========================================
--[[
Quicksaves used:
qs1 - Start pose
qs2 - savebest
qs4 - save recentbest
qs5 - max.loss- best critter score
qs98 - filter score
qs99 - filter score
]]--
--=========================================
DEBUGRUN = false
function dbgprint( debug_msg )
--dbgmsg must be entirely string
if DEBUGRUN then
print( debug_msg )
end
end--Thnx to KarenCH
function HasLigandCheck( )
alwaysLigand = false
segCnt2 = segCnt
while structure.GetSecondaryStructure(segCnt2)=="M" do segCnt2 = segCnt2-1 end
HASLIGAND = segCnt2 < segCnt
if HASLIGAND then
LIGAND = segCnt
alwaysLigand = true
end
end
function HasMutableCheck( )
HASMUTABLE = false
for i = 1, segCnt do
if structure.IsMutable( i ) then
HASMUTABLE = true
break
end
end
end
function SymCheck()
local is_sym = structure.GetSymCount()
if is_sym >0 then
SYMMETRICBANDS = true
else
SYMMETRICBANDS = false
end
return is_sym
end
function Score( )
return current.GetEnergyScore( )
end
---------------------------------------------
----------------- CI STUFF ------------------
---------------------------------------------
--WF = 1 --INT, user set- more wiggles factor
--wf --INT, used in wiggle function only if maxCI is true
--userSetMaxCI --FLOAT, user preferred value
--maxCI --BOOL, used in wiggle function -boolean flag to use WF value
--CapCI --BOOL, if true cap CI to userSetMaxCI else multiply recipeCI value by userSetMaxCI
--RecipeCI --FLOAT, value that recipe sets
function CI( RecipeCI )-- Cap CI else relative CI
if RecipeCI > 0.99 then maxCI = true else maxCI = false end
local Using_CI = RecipeCI
if CapCI == true then
if RecipeCI > userSetMaxCI then
Using_CI = userSetMaxCI
end
else
Using_CI = RecipeCI * userSetMaxCI -- relative CI
end
if Using_CI < 0 or Using_CI > 1 then--sanity check
print( 'CI ERROR, CI now set to 1' )
Using_CI = 1
end
--print('Recipe set CI', RecipeCI, ' Using_CI ', Using_CI )
behavior.SetClashImportance( Using_CI )
end-- Thnx to TvdL
---------------------------------------------
-------------- FILTER STUFF -----------------
---------------------------------------------
--
------------
-- Module Filteractive
-- First step: check if Filters are active
TvdLscore = current.GetEnergyScore()
behavior.SetSlowFiltersDisabled( true )
TvdLscore2 = current.GetEnergyScore()
behavior.SetSlowFiltersDisabled( false )
TvdLmaxbonus = TvdLscore - TvdLscore2
function TvdLFilter()
local ask=dialog.CreateDialog("Slow filters seem to be active")
ask.disable=dialog.AddCheckbox("Run with disabled slow filters",TvdLFilteractive)
ask.l1=dialog.AddLabel("Current bonus is: "..TvdLmaxbonus)
ask.l2=dialog.AddLabel("If this is not the maximum bonus put in a number")
if TvdLmaxbonus < 0 then TvdLmaxbonus=0 end
ask.maxbonus=dialog.AddTextbox("Set maxbonus:",TvdLmaxbonus)
ask.l3=dialog.AddLabel("Scores will only be checked for real gains if")
ask.l4=dialog.AddLabel("Score with filter off+maxbonus is a potential gain")
ask.continue=dialog.AddButton("Continue",1)
dialog.Show(ask)
TvdLmaxbonus= ask.maxbonus.value
if TvdLmaxbonus == "" then TvdLmaxbonus = 0 end
TvdLFilteractive=ask.disable.value
end
BetterRecentBest=false
function FilterOff()
-- Filters off but restore a better recentbest with filter off
behavior.SetSlowFiltersDisabled(true)
if BetterRecentBest then
save.Quicksave(99)
save.Quickload(98)
recentbest.Save()
save.Quickload(99)
end
end
function FilterOn()
-- Filter on but remember recent best if better than current
BetterRecentBest= recentbest.GetEnergyScore() > current.GetEnergyScore()
if BetterRecentBest then
save.Quicksave(99)
recentbest.Restore()
save.Quicksave(98)
save.Quickload(99)
end
behavior.SetSlowFiltersDisabled(false)
end
TvdLFilteractive=(math.abs(TvdLmaxbonus) > 0.1)
if TvdLFilteractive then
--Filters active, give people a choice
--And ask what the maximum bonus is.
TvdLFilter()
end
-- End of module Filteractive --
--
-- -
--
bestScore=Score()
if TvdLFilteractive then FilterOff() end
function SaveBest()
if (not TvdLFilteractive) or
(Score() + TvdLmaxbonus > bestScore ) then
if TvdLFilteractive then FilterOn() end
local g = Score() - bestScore
if g > MINGAIN then
if g > 0.01 then print("++Gained another "..round(g).." pts.", " to:", round( Score() ) ) end
bestScore = Score()
save.Quicksave( 2 )
end
if TvdLFilteractive then FilterOff() end --end of function-- add this filter
end
end
function DoFuze( )-- added filter here as well
if (not TvdLFilteractive) or
(Score() + TvdLmaxbonus > bestScore ) then
if TvdLFilteractive then FilterOn() end
local g = Score() - bestScore
if g > fuzeThresh and noFuze == false then
FuzeWithCuts()
end
if TvdLFilteractive then FilterOff() end -- end of function -- add this filter
end
end
function SaveRB( )-- added filter here as well
if (not TvdLFilteractive) or (current.GetEnergyScore()+TvdLmaxbonus> bestScore) then
if TvdLFilteractive then FilterOn() end
local rb = 0
if energy == true then
rb = recentbest.GetEnergyScore( )
else
rb = recentbest.GetScore( )
end
if rb > bestScore then
save.Quicksave( 4 )
recentbest.Restore( )
SaveBest( )
save.Quickload( 4 )
end
if TvdLFilteractive then FilterOff() end
end
end-- Thnx to Timo van der Laan
----------- END FILTER STUFF -----------------
----------------------------------------------
-- -------------------------------------------
-- -------- STANDARD FUNCTIONS ---------------
-- -------------------------------------------
function SelectSphere(sg, radius)
--selection.DeselectAll()
for i=1, segCnt do
if structure.GetDistance(sg,i)<radius then selection.Select(i) end
end
end--TvdL
--
---
--
function uncut(seg)
if seg == nil then
print('A nil segment is ',seg)
print('uncutting all segments')
for i = 1, structure.GetCount( ) do
structure.DeleteCut( i )
end
else
--print( 'Deleting cut at seg:',seg )
structure.DeleteCut( seg )
end
end
--
---
--
function WigglePowerShowSettting( )
local get_wig_pow = behavior.GetWigglePower( )
local wigdict={ ["l"] = "Low", ["a"] = "Auto", ["m"] = "Medium", ["h"] = "High" }
local result = wigdict[ get_wig_pow ]
return result
end
--
---
--
function CleanUseSegments( )--add check for doubles ???
--sanity check on segnums, "never trust user input"
--this really needs a minimum length what if theres only 5 segments ??
for i = #UseSegments, 1, -1 do
local seg = UseSegments[i]
if seg > segCnt then
print( 'ERROR segment too high: removing segment', seg )
table.remove( UseSegments, i )
end
end
for ii = #UseSegments, 1, -1 do --table remove can change table len and indexes
local seg = UseSegments[ii]
if seg <= 0 then
print( 'ERROR segment too low: removing segment', seg )
table.remove( UseSegments, ii )
end
end
print("How many residues", segCnt )
if #UseSegments ==0 then
print('Random segment selection from '.. segCnt ..' segs')
else
print("Using this many segs", #UseSegments)
end
end
--
----
--
function DeleteBands( )
--add deleteeAll ??
nbands = band.GetCount( )
if nbands > USERBANDS then
for i = nbands, USERBANDS + 1, -1 do -- count down from nbands to USERBANDS + 1
band.Delete( i )
end
end
end -- thanks to Jeff101
--
----
--
function round( x )-- round to 3 places
return x - x % 0.001
end
function down( x )-- round down to 1 place
return x-x % 1
end
--
----
--
function SeedRandom( )
local seed = os.time ( ) / math.abs ( current.GetEnergyScore ( ) )
seed = seed % 0.001
seed = 1 / seed
while seed < 10000000 do
seed = seed * 1000
end
while seed > 2 ^ 32 do
seed = seed / 10
end--LO
seed = seed - seed % 1
math.randomseed( seed )
math.random( ); math.random( ); math.random( ) --See stackoverflow
end-- Rav3n_pl, LociOiling
function Randomz( n1, n2 ) --random function returns int or float depends on input vars --can return 0
if n2 == nil and n1 == nil then
return math.random( ) --float
else
if n2 == nil then
if n1%1 == 0 then
return math.random( n1 ) --integer
else
return math.random( ) * n1 --float
end
else
if n1%1 == 0 and n2 % 1 == 0 then
return math.random( n1, n2 ) --integer between
else
return math.random( )*( n2-n1 ) + n1 --float between
end
end
end
end
--
-- -
--
function Cleanup( err )
if CLEANUPENTRY ~= nil then
return
end
CLEANUPENTRY = true --LO
print( '========================' )
print( ReVersion..' Cleaning up' )
save.Quickload( 2 )--best pose
DeleteBands( )
selection.DeselectAll()
print( "Base score",BASESCORE)
print( "Start score: "..ss )
print( "Final score: "..Score( ) )
print( "Total gain: "..round( Score( ) - ss ) )
if TvdLFilteractive then FilterOn( ) end
print( "Unfiltered score: "..current.GetEnergyScore( ) )
undo.SetUndo( true )
behavior.SetClashImportance(userSetMaxCI)--return to user setting
print( err )
end
-- ------- END STANDARD FUNCTIONS -----------
-- ------------------------------------------
-- ------------------------------------------
-- ------------------------------------------
--START quick shake algorithm
--not sure if this is relevant anymore now 1 shake iter pretty much is it but does take a while
function QuickShake()
--[[---------------------------------
-- Algorithm for faster shake
REFERENCES
1. N/A
Copyright ( C ) 2014 Seagat2011 <http://fold.it/port/user/1992490>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
( at your option ) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
$Id$
--------------------------------------------------------------------------]]--
--[[
v1
- Inception
1.2
- Loops until nogain
]]--
local idx = 1
local idx2 = structure.GetCount ()
local hash = {}
local cs = current.GetScore ()
--print ( 'Quick Shake Algorithm v1.2 SG2011 - Current Score: '..cs )
local function New ( obj )
return obj -- pass-new-obj-by-value
end
local _gses = New ( current.GetSegmentEnergySubscore )
local function SmartShake ( iters )
local gain = true
while ( gain ) do
gain = false
for i = idx, idx2 do
local ci = _gses ( i, 'clashing' )
if ci < _gses ( i, 'hiding' ) and
ci < _gses ( i, 'packing' ) and
ci < _gses ( i, 'ideality' ) and
ci < _gses ( i, 'sidechain' ) and
ci < _gses ( i, 'bonding' ) and
ci < _gses ( i, 'other' ) and
ci < _gses ( i, 'reference' ) then
selection.Select ( i )
end -- test ci --
end -- loop --
if selection.GetCount () then
local css = current.GetScore ()
structure.ShakeSidechainsSelected ( iters )
selection.DeselectAll ()
if current.GetScore () > css then
gain = true
end -- test --
end -- test --
end -- loopf --
end
structure.SmartShake = SmartShake
structure.SmartShake ( 1 )
end
-- ---- END quick shake algorithm -----------
---------------------------------------------
------------- WIGGLE FUNCTION ---------------
---------------------------------------------
-- Timos edrw302 Wiggle funtion
-- New WiggleFactor
-- Optimized due to Susumes ideas
-- Note the extra parameter to be used if only selected parts must be done
function Wiggle( how, iters, minppi, onlyselected ) -- take advantage of onlyselected parameter here
if how == nil then how = "wa" end
if iters == nil then iters = 3 end
if minppi == nil then minppi = 0.1 end
if onlyselected == nil then onlyselected = false end
local currentgetci = 1
local wf = 1
if maxCI then wf = WF end --if recipe CI is 1 then wiggle more
--print('Wiggle factors, wf,WF:',wf,WF, 'maxCI',maxCI, 'Wiggles',(2 * wf * iters) )--debug
local sp = Score()
-- -------------------------------------------------------------------------------------------
if onlyselected then
if how == "s" then
if mutateAlways == true then
currentgetci = behavior.GetClashImportance() --Note1
behavior.SetClashImportance(mutateCI)
structure.MutateSidechainsSelected( 1 )-- no shake !!
behavior.SetClashImportance( currentgetci )
else
dbgprint('Only Selected shake')
structure.ShakeSidechainsSelected( 1 )
--QuickShake()
return
end
elseif how == "wb" then structure.WiggleSelected( 2 * wf * iters, true, false )
elseif how == "ws" then structure.WiggleSelected( 2 * wf * iters, false, true )
elseif how == "wa" then structure.WiggleSelected( 2 * wf * iters, true, true )
end
else
-- -------------------------------------------------------------------------------------------
if how == "s" then
if mutateAlways == true then
currentgetci = behavior.GetClashImportance()
behavior.SetClashImportance(mutateCI)
structure.MutateSidechainsAll( 1 )
behavior.SetClashImportance( currentgetci )
else
--structure.ShakeSidechainsAll( 1 )
dbgprint('Selected All quickshake')
--if some are slected it will shake them then recurse
QuickShake()
return
end
elseif how == "wb" then structure.WiggleAll( 2 * wf * iters, true, false )
elseif how == "ws" then structure.WiggleAll( 2 * wf * iters, false, true )
elseif how == "wa" then structure.WiggleAll( 2 * wf * iters, true, true )
end
end
end-- TvdL
--Note1 "it is sometimes inevitable to write bad code"
--this needs sorting out
--Shake MutateOnce,MutateAlways the logic is all twisted together
-- --------- END WIGGLE FUNCTION -------------
-- -------------------------------------------
-- -------------------------------------------
---------------------------------------------
--------------- FUZES -----------------------
---------------------------------------------
function FuzeEnd( )
CI( 1 )
Wiggle( "wa", 1 )
Wiggle( "s", 1 )--mutateAlways and all are selected
Wiggle( )
srb( )
end
function Fuze1( ci1, ci2 )
CI( ci1 )
Wiggle( "s", 1 )--mutateAlways
CI( ci2 )
Wiggle( "wa", 1 )
end
function Fuze2( ci1, ci2 )
CI( ci1 )
Wiggle( "wa", 1 )
CI( 1 )
Wiggle( "wa", 1 )
CI( ci2 )
Wiggle( "wa", 1 )
end
function srb( )
recentbest.Restore( )
SaveBest( )
end
function Fuze( )--is Fuze supposed to be this big ??its like 16 shake,wiggle cycles
print( "Fuzing..." )
selection.SelectAll( )
recentbest.Save( )
Fuze1( 0.3, 0.6 )
FuzeEnd( )
Fuze2( 0.3, 1 )
srb( )
Fuze1( 0.05, 1 )
srb( )
Fuze2( 0.7, 0.5 )
FuzeEnd( )
Fuze1( 0.07, 1 )
srb( )
end
function FuzeCuts(ci1)
local seg = math.random( structure.GetCount() ) -- check for locked frozen ??
Cut( seg )
CI( 1 )
Wiggle( "wa", 15 )
CI( ci1 )
Wiggle( "wa",1 )
CI( 1 )
Wiggle( "wa", 25 )
uncut( seg ) --DeleteCut
recentbest.Save( ) --to avoid cuts in recentbest pose
Wiggle( "wa", 25 )
CI( ci1 )
Wiggle( "wa", 2 )
CI( 1 )
Wiggle( "wa", 25 ) -- good grief thats a lot of wiggling
end-- pauldunn
function FuzeWithCuts()
print("Fuzing with cuts...")
selection.SelectAll()
recentbest.Save()
Fuze1(0.3,0.6)
FuzeEnd()
FuzeCuts(0.01)
srb()
Fuze2(0.3,1)
srb()
Fuze1(0.05,1)
srb()
Fuze2(0.7,0.5)
FuzeEnd()
FuzeCuts(0.1)
srb()
Fuze1(0.07,1)
srb()
end
-- ------------ END FUZES ------------------
-- -------------------------------------------
-- -------------------------------------------
-- ------------ GENETIC ALGORITHM ------------
-- -------------------------------------------
function CanBeUsed( sg1, sg2 ) --checking end of bands
local function AreGood( s1, s2 ) --check that s1 and s2 can be used
--cant we just return false instead of setting ok to false ??
local ok = true
if s1 == s2 then ok = false end -- no band between same segments
if s1 > segCnt or s2 > segCnt then ok = false end --sanity check out of bounds seg
if s1 <= 0 or s2 <= 0 then ok = false end --sanity check out of bounds seg
if s2 - s1 < bands.minSkip then ok = false end
if ok == true and structure.GetDistance( s1, s2 ) < bands.minDist then ok = false end -- 1st condition ???
local lok1 = structure.IsLocked( s1 ) -- true if locked
local lok2 = structure.IsLocked( s2 ) -- double lock check
local bothlocked = lok1 and lok2
if bothlocked == true then ok = false end --bothlocked = true if both are locked
return ok
end
local ok = AreGood( sg1, sg2 )
-- --------------------------------------------------------------------------------------
-- this needs to be in dialog plus the range code to go with it
if ok == true and #DoNotUse > 0 then --none of 2 can be in that area
for i = 1, #DoNotUse do
local r = DoNotUse[i]
for x = r[1], r[2] do
if x == sg1 or x == sg2 then
ok = false
break
end
end
if ok == false then break end
end
end
-- --------------------------------------------------------------------------------------
-- if theres a Ligand 'M' no idea what will happen
local function ssCheck( ss )
local good = false
if use.Sheet and ss == "E" then good = true end
if use.Helix and ss == "H" then good = true end
if use.Loop and ss == "L" then good = true end
return good
end
if ok == true then --check structure
local check_struct = use.Sheet and use.Helix and use.Loop
if check_struct == false then --has check_structure been enabled
ok = false
local ss1 = structure.GetSecondaryStructure( sg1 )
local ss2 = structure.GetSecondaryStructure( sg2 )
if checkBoth then
if ssCheck( ss1 ) and ssCheck( ss2 ) then ok = true end
else
if ssCheck( ss1 ) or ssCheck( ss2 ) then ok = true end
end
end
end
return ok
end
-- --------------------------------------------------------------------------------------
function SortCritters( ) --bubble sort
for i = 1, #critters do
for j = i + 1, #critters do
if critters[i].score < critters[j].score then
critters[i], critters[j] = critters[j], critters[i] --love lua : )
end
end
end
end
function ShuffleHerd( )
if herd.shuffle == true then
for i = 1, #critters do
local r = Randomz( #critters )
if r ~= i then
critters[i], critters[r] = critters[r], critters[i]
end
end
end
end
function FillHerd( )
local n = #critters
if n > 0 then --fill up herd
n = herd.newRandom
else --fresh herd
n = herd.breedBest + herd.keepBest + herd.breedBastards
end
print( "Randomizing "..n.." new critters..." )
for i = 1, n do
AddCritter( i, n )
end
end
function KeepGood( ) --copy best scoring critters form last gen if score above
local newHerd = {}
for i = 1, herd.keepBest do
if critters[i].score > critter.keepScore and ( math.abs( critters[i].score ) > 0.1 or critters[i].score > 0 ) then
newHerd[#newHerd + 1] = critters[i]
end
end
return newHerd
end
function AddCritter( cnum, n ) --create new random critter
local c = {}
critterID = critterID + 1
c.no = critterID
c.name = c.no..'-rnd'
c.bands = {}
local r = Randomz( critter.minBands, critter.maxBands )
for i = 1, r do
c.bands[#c.bands + 1] = AddBand( )
end
critters[#critters + 1] = c
--print( '( '..cnum..'/'..n..' ) '..c.name.." bands: "..#c.bands )
end
-- ====================================================================================================================================
-- here 1 end of band is taken from random or onlyMutable or UseSegments
-- then the other end is taken from UseSegments or random then checked with CanBeUsed
--sym banding : lengths??, i thnk this could use more work
function AddBand( ) --create one random band
local cnt = 0
local b = {}
while true do --try till die
cnt = cnt + 1
local branch = Randomz(structure.GetSymCount() +1)-1 -- 0 if monomer, equal probability for each branch 2020 bk
-- ----------------------------------
local s1 = Randomz( segCnt ) -- assign random segment to s1
if onlyMutable == true or #UseSegments > 0 then -- overwrite s1 with new value if either conditions is true
s1 = UseSegments[Randomz( #UseSegments )] -- select seg from UseSegments
end
--hold on if onlyMutable is true tehn usesegments will always be >0 because then it loads UseSEGMNETS with onlymutable segs
if alwaysLigand then
s1 = LIGAND
end
-- --------------------------------------------------------------------------------------------------------------------------
local s2 = Randomz( segCnt ) -- assign random segment to s2
if onlyMutable == true then -- overwrite s1 with new value if onlymutable is true
s2 = UseSegments[Randomz( #UseSegments )] -- select seg from UseSegments
end
if s1 > s2 then s1, s2 = s2, s1 end -- ensure s1 is lower than s2
if CanBeUsed( s1, s2 ) then -- check segments are valid
local str = Randomz( bands.minStr, bands.maxStr ) -- set band strength
local len = 0
while true do --randomize correct distance
len = Randomz( -bands.maxDn, bands.maxUp )
if len < -bands.minChng or len > bands.minChng then break end --dont understand this
end
if band_Oligomer then
b = { s1, s2, str, len, branch} --2020 symmetry bands bk
else
b = { s1, s2, str, len}
end
break -- break while loop when good band created
end
if cnt > 750 then
print( "Sorry! Cant create band! Breaking script!" )
BreakScript( ) --deliberately no such function, so it terminates recipe.
end
end
return b
end
-- ------------------------------------------------------------------------------------------
function ScoreHerd( ) --score all critters from herd
local selectedsegs = {}
save.Quickload( 2 ) -- best pose
print( "Scoring "..#critters.." critters..." )
save.Quicksave( 5 ) --Aah SAVE into quicksave 5..Wait..no..that still makes no sense
local herdScore = Score( )
for i = 1, #critters do
DeleteBands( )
local crt = critters[i] --critter
local CritterBaseScore = Score( ) --start score
local bnds = crt.bands -- b={s1,s2,str,len, // branch} (len is the random push or pull difference of length)
for b = 1, #bnds do
local bnd = bnds[b]
if bnd[1] ~= bnd[2] then -- no band to itself -redundant now cos of check segs
local a1 = 5 -- atom1 central carbon atom
local a2 = 5 -- atom2 central carbon atom
if bnd[1] == structure.GetCount( ) then a1 = 6 end -- final carbon atom is + 1
if bnd[2] == structure.GetCount( ) then a2 = 6 end
if structure.GetAminoAcid( bnd[1] ) == 'g' then a1 = 0 end -- glycine has no beta-carbon .alpha carbon for g is atom 2 ??
if structure.GetAminoAcid( bnd[2] ) == 'g' then a2 = 0 end
if not structure.IsHydrophobic( bnd[1] ) and bnd[4]<0 then a1 = 0 end -- only hiding hydrophobics --hows this work??
if not structure.IsHydrophobic( bnd[2] ) and bnd[4]<0 then a2 = 0 end
if structure.IsHydrophobic( bnd[1] ) and bnd[4]>0 then a1 = 0 end -- don't push hydropobics to water--??
if structure.IsHydrophobic( bnd[2] ) and bnd[4]>0 then a2 = 0 end
-- if atom is 0 what does that do--think it bands to alpha carbon so it dont rotate as freely
if structure.GetSecondaryStructure( bnd[1] ) == 'M' then a1 = structure.GetAtomCount(bnd[1]) end--rg ligand hack
if structure.GetSecondaryStructure( bnd[2] ) == 'M' then a2 = structure.GetAtomCount(bnd[2]) end--rg ligand hack
--this will crash foldit if atom count is wrong which is unlikely but...
-- should really wrap this in safe pcall function
if band_Oligomer then
band.AddBetweenSegments( bnd[1], bnd[2], a1, a2, bnd[5] ) -- 5 is the branch for symmetrics 2020 bk
else
band.AddBetweenSegments( bnd[1], bnd[2], a1, a2 ) -- this has return value thats sort of checked by 'bc'
end
local bc = band.GetCount( )
if bc > 0 then
selectedsegs[#selectedsegs+1] = bnd[1] -- store banded segs
selectedsegs[#selectedsegs+1] = bnd[2] -- store banded segs
band.SetStrength( bc, bnd[3] )
local len = structure.GetDistance( bnd[1], bnd[2] ) + bnd[4]-- distance between segments + band length change ( + or - )
if len < bands.minLen then len = bands.minLen end
band.SetGoalLength( bc, len )
--print( "Band: "..bnd[1].."-"..bnd[2].." a1: "..a1.." a2: "..a2.." str: "..round( bnd[3] ).." length = "..round( length ) )
end
end -- if bnd[1]
end -- for b
-- -----------------------------------------------------
CI( pullCI )
recentbest.Save( )
local seg = math.random( structure.GetCount( ) ) -- hmm no cuts in locked or frozen
Cut( seg )
Wiggle( "wb", 1 ) --this now does 2 iters 'cos of way wiggle() works
uncut( seg )
recentbest.Save() -- to avoid recent best in cuts --why here wont recent best now have bands in ???
--great all this kerfuffle for basically these few ilnes
DeleteBands( )
-- ------------------------------------------------------
-- select a sphere of segs from band origin point
if SmallMutate then
--print('Len of selected segs',#selectedsegs)
for k, v in ipairs( selectedsegs ) do
SelectSphere(v, ShakMutRadius )
end
else
selection.SelectAll( )
end
-- ------------------------------------------------------
if mutateOnce == true then
CI( mutateCI )
structure.MutateSidechainsSelected( 1 )
end
--Fast Qstab
CI( shakeCI ) --was 0.1
Wiggle( "s", 1, nil, true ) --Now always shake selected, if mutatealways then mutate
-- plus a qstab threshold ?? threshold should be a percentage
if noQstab == false then
dbgprint('doing Qstab')
CI( 0.4 )
Wiggle( "wa", 1 )
CI( 1 )
Wiggle( "s", 1 )--mutateAlways
end
CI( 1 )
undo.SetUndo( true ) -- UNDO - So undo graph has at least seomthing in it
Wiggle( )
undo.SetUndo( false ) -- UNDO
-- ---------------------------------------------------------------------------
--use filters for fuze check and fuze if required
--fuze selects all
--2 shakes at least so 2 calls to mutatealways with all selected
DoFuze()
SaveRB( )
-- ---------------------------------------------------------------------------
selection.DeselectAll( )--rg
crt.score = Score( ) - CritterBaseScore
print( "( "..i.."/"..( #critters ).." ) Critter "..crt.name.." : "..round( crt.score ),round( Score() ) )
-- ---------------------------------------------------------------------------
--Not checked but --this needs sorting out filters trash Score()
--how does this work anyway ???
--quicksave 5 is never integrated into quicksave 2
--What is this supposed to do ??
if critter.maxLoss > 0 then
--print('maxloss', Score(), herdScore)
if Score( ) > herdScore - critter.maxLoss then
save.Quicksave( 5 )
herdScore = Score( )
else
save.Quickload( 5 )
end
else
save.Quickload( 2 )
end
--dbgprint('#selectedsegs'..#selectedsegs)
for key in pairs ( selectedsegs ) do --empty selectedsegs table but preserve reference
selectedsegs[key] = nil
end
-- ---------------------------------------------------------------------------
end -- for i
save.Quickload( 2 )
if band.GetCount( ) > 0 then --clean bands from best solution ( if any )
DeleteBands( )
save.Quicksave( 2 )
end
end
function BreedCritters( mom, dad, t ) --breed 2 critters. bands are taken randomly from parents
local kid = {}
critterID = critterID + 1
kid.no = critterID
kid.name = kid.no.."-"..t..mom.no..'/'..dad.no
kid.bands = {}
local mb = #mom.bands
local db = #dad.bands
if mb > db then mb, db = db, mb end -- kids have band count between mom and dad band count
local bn = Randomz( mb, db ) -- random num bands, min=mom , max= dad
for i = 1, bn, 2 do -- every other
kid.bands[#kid.bands + 1] = mom.bands[Randomz( #mom.bands )] -- random chromosome selection
kid.bands[#kid.bands + 1] = dad.bands[Randomz( #dad.bands )]
end
--print( kid.name.." bands: "..#kid.bands )
return kid
end
function BreedHerd( )
print( "Breeding..." )
SortCritters( )
newHerd = KeepGood( )
for i = 1, herd.breedBest do
local mom = critters[i]
if mom.score > critter.breedScore or i < 2 then --breed only good ones, 1st is always breed anyway
for j = i + 1, herd.breedBest do
local dad = critters[j]
newHerd[#newHerd + 1] = BreedCritters( mom, dad, "kid-" )
newHerd[#newHerd + 1] = BreedCritters( dad, mom, "kid-" )
end
end
end
for i = 1, herd.breedBastards do --they will always appear ;]
local mom = critters[i]
local j = Randomz( herd.breedBastards + 1, #critters )
local dad = critters[j]
newHerd[#newHerd + 1] = BreedCritters( mom, dad, "bas-" )
newHerd[#newHerd + 1] = BreedCritters( dad, mom, "bas-" )
end
critters = newHerd
FillHerd( )
end
--====================================================================
function LoadUseSegmentsList()
--How to combine the logic of DoNotUse, AlwaysUse, UseSegments and OnlyMutable ???
if onlyMutable == true then
for i = 1, segCnt do
if structure.IsMutable( i ) then
UseSegments[#UseSegments+1] = i
end
end
end
if #AlwaysUse > 0 then --this WILL add dupes to UseSegments, slack but not really a problem
for i = 1, #AlwaysUse do
local ss = AlwaysUse[i][1]
local se = AlwaysUse[i][2]
if ss > se then ss, se = se, ss end
for j = ss, se do
UseSegments[#UseSegments+1] = j
end
end
AlwaysUse={} --added to list, canbeused check for this is now redundant
--now these segs are in canbeused then they MIGHT be used in a critter
--rather than they should ALWAYS be 1 from this list per critter
--Is this implemented wrongly ?? Its not like this in GABBiS
end
end
-- -----------------------------------------------------
function GAB( )
LoadUseSegmentsList()
CleanUseSegments( ) -- bounds check on seg nums
critterID = 0
gen = 0
ss = Score( )
ssstr = ( round( ss ) )
save.Quicksave( 2 )
recentbest.Save( )
print( "Start score: "..round( ss ) )
critters = {}
FillHerd( )
badGen = 0
while true do --this is ( almost ) endless script ;]
genScore = Score( )
gen = gen + 1
print( )
print( "Generation: "..gen.." ( "..( os.date( ) ).." )")
print("Score: "..round( Score( ) )..", gain: "..round( Score() -ss ) )
ShuffleHerd( )
ScoreHerd( )
save.Quickload( 2 )
if gen == herd.maxGen then break end --end of script
if genScore >= Score( ) then badGen = badGen + 1 else badGen = 0 end --fixed
if badGen >= herd.renew then
print( " ** Creating fresh random herd **" )
critters = {}
FillHerd( )
badGen = 0
else
BreedHerd( )
end
genEndScore = Score( )
genGain = genEndScore - genScore
if gen > 1 and ( gen-1 ) == 5 * down( ( gen-1 )/5 ) then
ssstr = ( ssstr..'\n ' ) -- start new line every 5 generations
end
if genGain >= 0 then
ssstr = ( ssstr..' + '..round( genGain ) )
else
ssstr = ( ssstr..' - '..( -round( genGain ) ) )
end
print( ssstr..' = '..round( genEndScore ) )
end -- while true
save.Quickload( 2 ) -- best pose
DeleteBands( )
selection.DeselectAll()
if TvdLFilteractive then FilterOn( ) end
print( "Final score: "..round( Score( ) ).." Total gain: "..round( Score( ) - ss ) )
end -- GAB( )
--
-- -
--
-- -------------------------------------------
-- ----------------- DIALOGS -----------------
-- -------------------------------------------
function MoreDialogOptions( )
local opt2 = dialog.CreateDialog( ReVersion )
opt2.spacer_0 = dialog.AddLabel( "More Options." )
opt2.fuzeth = dialog.AddTextbox( "Fuze threshold: ", fuzeThresh )
opt2.spacer_1 = dialog.AddLabel( " " )
opt2. mutsmall = dialog.AddCheckbox("Use fewer segs :: affects Shake and Mutate", SmallMutate )
opt2.spacer_2 = dialog.AddLabel( "Sphere size for use fewer segs " )
opt2. shakmutrad = dialog.AddSlider( "Sphere", ShakMutRadius, 1,16,0 )
opt2.mutci = dialog.AddSlider( "Mutate CI", mutateCI , 0.1, 1, 2 ) -- mutate on what clashing importance
opt2.minimumgain = dialog.AddSlider( "Min Gain", MINGAIN, 0, 75, 0 )
opt2.spacer_3 = dialog.AddLabel( " " )
opt2.run = dialog.AddButton( "Back", 1 )
dialog.Show( opt2 )
fuzeThresh = tonumber( opt2.fuzeth.value )
MINGAIN = opt2.minimumgain.value
SmallMutate = opt2.mutsmall.value
ShakMutRadius = opt2.shakmutrad.value
mutateCI= opt2.mutci.value
end
--
-- -
--
function GADialogOptions( )
local opt1 = dialog.CreateDialog( "Genetic Algorithm Options" )
opt1.lbl1 = dialog.AddLabel( "Herd:" )
opt1.brbest = dialog.AddSlider( "Breed best", herd.breedBest, 1, 20, 0 )
opt1.kbest = dialog.AddSlider( "Keep best", herd.keepBest, 1, 20, 0 )
opt1.bas = dialog.AddSlider( "Breed bastards", herd.breedBastards, 1, 20, 0 )
opt1.rnd = dialog.AddSlider( "New random", herd.newRandom, 1, 30, 0 )
opt1.renew = dialog.AddSlider( "Renew herd", herd.renew, 1, 10, 0 )
opt1.shuff = dialog.AddCheckbox( "Shuffle critters", herd.shuffle )
opt1.lbl2 = dialog.AddLabel( "Critter:" )
opt1.minb = dialog.AddSlider( "Minimum bands:", critter.minBands, 1, 4, 0 )
opt1.maxb = dialog.AddSlider( "Maximum bands:", critter.maxBands, 4, 50, 0 )
opt1.keeps = dialog.AddSlider( "Keep score:", critter.keepScore, -10, 10, 0 )
opt1.breeds = dialog.AddSlider( "Breed score:", critter.breedScore, -40, 10, 0 )
opt1.maxloss = dialog.AddSlider( "Max loss:", critter.maxLoss, 0, 50, 0 )
opt1.lbl4 = dialog.AddLabel( "( 0 to disable max loss )" )
opt1.lbl3 = dialog.AddLabel( "Bands:" )
opt1.minst = dialog.AddSlider( "Min str", bands.minStr, 0.1, 2, 1 )
opt1.maxst = dialog.AddSlider( "Max str", bands.maxStr, 0.2, 2, 1 )
opt1.maxup1 = dialog.AddSlider( "Max length", bands.maxUp, 1, 20, 0 )
opt1.minlen = dialog.AddSlider( "Min length", bands.minUp, 0, 6, 0 )
opt1.run = dialog.AddButton( "Back", 1 )
dialog.Show( opt1 )
herd.breedBest = opt1.brbest.value
herd.keepBest = opt1.kbest.value
herd.breedBastards = opt1.bas.value
herd.newRandom = opt1.rnd.value
herd.shuffle = opt1.shuff.value
herd.renew = opt1.renew.value
critter.minBands = tonumber( opt1.minb.value )
critter.maxBands = tonumber( opt1.maxb.value )
critter.keepScore = tonumber( opt1.keeps.value )
critter.breedScore = tonumber( opt1.breeds.value )
critter.maxLoss = tonumber( opt1.maxloss.value )
minStr = opt1.minst.value --minimum band str
maxStr = opt1.maxst.value --maximum band str
maxUp = opt1.maxup1.value --maximum change up ( push ) :: maximum band len
minLen = opt1.minlen.value --minimum length of created band
end
--
-- -
--
function DialogOptions( )
local dialogresult
local opt = dialog.CreateDialog( ReVersion )
repeat
opt.lbl1 = dialog.AddLabel( ReVersion.." main options:" )
opt.gen = dialog.AddTextbox( "Generations:", herd.maxGen )
opt.WF = dialog.AddSlider( "WiggleFactor:",WF, 1, 5, 0 )
opt.pull = dialog.AddSlider( "Pulling CI", pullCI, 0.05, 1, 2 )--description, default, min, max, precision
opt.shak = dialog.AddSlider( "Shake CI", shakeCI, 0.01, 1, 2 )
opt.capci = dialog.AddCheckbox( "Cap CI, Otherwise Relative CI", CapCI )
opt.usermaxci = dialog.AddSlider( "Maximum CI", userSetMaxCI, 0.05, 1, 2 )
opt.skipqstab = dialog.AddCheckbox( "Skip Qstab", noQstab ) --put in 'More options' ?
opt.skipfuze = dialog.AddCheckbox( "Skip Fuze", noFuze ) --put in 'More options' ?
opt.spacer1 = dialog.AddLabel( " " )
if HASMUTABLE then
opt.mutonly = dialog.AddCheckbox( "Only mutable", onlyMutable )
opt.mutonce = dialog.AddCheckbox( "Mutate once", mutateOnce )
opt.mutalways = dialog.AddCheckbox( "Mutate always", mutateAlways )
opt.spacer3 = dialog.AddLabel( " " )
end
if HASLIGAND then
opt.hasligand = dialog.AddCheckbox("Always use LIGAND ", alwaysLigand )
end
if SYMMETRICBANDS then
opt.AllowSymmetrybands = dialog.AddCheckbox("Allow symmetry bands: ", band_Oligomer )
opt.spacer4 = dialog.AddLabel( " " )
end
if USERBANDS > 0 then
opt.label2 = dialog.AddLabel( " !! User bands found !!" )
opt.keepbands = dialog.AddCheckbox( "Keep user bands", keep_bands )
opt.spacer2 = dialog.AddLabel( " " )
end
opt.run = dialog.AddButton( "Start", 1 )
opt.more = dialog.AddButton( "More", 2 ) -- threshold and such
opt.morega = dialog.AddButton( "GA", 3 ) -- want most GA and/or numeric params in here
opt.cancel = dialog.AddButton( "Cancel", 0 )
dialogresult = dialog.Show( opt )
if dialogresult > 0 then
herd.maxGen = tonumber( opt.gen.value )
WF = opt.WF.value
pullCI = opt.pull.value
shakeCI = opt.shak.value
CapCI = opt.capci.value
userSetMaxCI = opt.usermaxci.value
noQstab = opt.skipqstab.value -- negative logic..makes Robert Martin cry :'-(
noFuze = opt.skipfuze.value -- negative logic
if USERBANDS > 0 then
keep_bands = opt.keepbands.value
end
if HASMUTABLE then
onlyMutable = opt.mutonly.value
mutateOnce = opt.mutonce.value
mutateAlways = opt.mutalways.value
end
if HASLIGAND then
alwaysLigand = opt.hasligand.value
end
if SYMMETRICBANDS then
band_Oligomer = opt.AllowSymmetrybands.value
end
if dialogresult == 2 then
MoreDialogOptions( )
end
if dialogresult == 3 then
GADialogOptions( )
end
end
until dialogresult < 2
return dialogresult
end
-- -------------------------------------------
-- -------------- END DIALOG -----------------
-- -------------------------------------------
function Prelim( )
--wonder if lesser values are changed from default they can be flagged and printed
print( ReVersion )
print( 'puzzle '..( puzzle.GetName( ) ) )
save.Quicksave( 1 ) --OG backup- after dialog so if cancelled in dialog these positions are still intact
save.Quicksave( 2 ) --best pose
print('Start pose in Quicksave 1')
print('Best pose in Quicksave 2') -- changed to slot 2
print('Generations to run:', herd.maxGen)
if MINGAIN > 1 then print ("Minimum Gain is set to:", MINGAIN) end
if USERBANDS > 0 then
if keep_bands then
print( '= Using '..USERBANDS..' user-supplied bands.' )
else
print( '= REMOVING '..USERBANDS..' user-supplied bands.')
USERBANDS = 0
band.DeleteAll()
end
end
if CapCI then print('= CI is capped at:', userSetMaxCI) else print('= CI is relativised from:', userSetMaxCI) end
local wigglepowuh = WigglePowerShowSettting( )
print("= Wiggle power :", wigglepowuh )
print('= Wiggle factor:', WF)
if noFuze == true then
print("= Fuze is: OFF")
else
print("= Fuze is: ON :: threshold is:", fuzeThresh)
end
if noQstab == true then
print("= Qstab is: OFF")
else
print("= Qstab is: ON")
end
if HASMUTABLE then
print('= MUTABLE puzzle')
if mutateOnce or mutateAlways then
print('= Mutate is: ON')
else
print('= Mutate is: OFF')
end
end
if HASLIGAND then
print('= LIGAND puzzle')
print('= LIGAND is :',LIGAND)
if alwaysLigand then
print('= Always use LIGAND is: ON' )
else
print('= Always use LIGAND is: OFF')
end
end
if SYMMETRICBANDS then
local is_sym = SymCheck()
is_sym = is_sym +1
print('= SYMMETRIC puzzle with C'..is_sym..' symmetry')
if band_Oligomer then
print('= Symmetry banding is: ON')
else
print('= Symmetry banding is: OFF')
end
end
end
-- -------------------------------------------------------
-- main call
function Main( )
HasMutableCheck( ) -- toggles global HASMUTABLE
SymCheck( ) -- toggles global SYMMETRICBANDS
HasLigandCheck( )
SeedRandom( )
local go = DialogOptions( )
if go == 1 then
Prelim( ) --print the settings
GAB( )
else
print( 'Cancelled' )
return
end
undo.SetUndo( true )
behavior.SetClashImportance( userSetMaxCI )-- return to user setting
end
-- -------------------------
xpcall( Main, Cleanup )
--GAB_proj_beta_v1.3.2
-- ---------- END RECIPE --------------------
-- ------------------------------------------
-- ------------------------------------------