Code
--[[
Bands! Bands in space!
GAB - Genetic Algorythm on Bands
by Rav3n_pl
based on CartoonVillan and Crashguard303 scripts
Lua V2
Definitions:
band: random band to space from random segment
critter: set of bands
herd: set of critters
1.
- randomize needed bands
- randomly assigning them to criters
2.
- score each critter
3.
- keep critters scoring above score
- breed best critters
- breed bastards (best one + random one)
- forget about rest of critter
- randomize new critters to fill herd
porky added the filter management
BK added more filters on recentbest and save
ver 2.02 Filter
--debugged line 239
ver 2.03
--debugged filter
ver 2.04
--debuggeg monomer
ver 2.05
-- just update the commands Get/SetFiltersDisabled
ver 2.06
-- debugged generic filter & second dialog
ver 2.1
-- second dialog with sliders (no text box). This reduces the range available
but it's also more convenient and it avoids user errors
ver 2.1.1 Generic filter only after dialog to save time at start
ver 2.1.2 Check conditions option on exploration puzzles
ver 2.1.3 debugged conditions
ver 2.2.0 added Jeff101's display on each loop, and archive in note by pauldunn
keep user bands
FILTERMANAGE activated (disable filter during wiggle)
ver 2.2.1 bug fixed in loop_count
ver 2.2.2 added quickshake
ver 2.2.3 undo.SetUndo
ver 2.2.4 fixed possible unideal loop bug
ver 2.3 managing ligands, new wiggle
ver 2.3.1 fixed DoAlwaysUseLigand()
ver 2.4 sketchbook adapted (minGain)
ver 2.5 info on filters (?? is it implemented?)
ver 2.5.1 GRRR adapted filter setting to new lua commands: return2OriginalFilterSetting()
ver 2.5.2 small changes in info
ver 2.5.3 small changes related to filters and info
ver 2.6 working on detailed filters and bonusses / The filters settings are the default ones (unchecked ones are never activated)
The bonusses are doubled for selection
ver 2.6.1 fixed non filtered puzzles
ver 2.6.2 fixed parameter filtering, added DEBUGprint
ver 2.6.3 added "Objectives" for identifying filtered puzzles
ver 2.6.4 filter tables "false" corrected
ver 2.6.5 fixed a TopScore bug
ver 2.7 with probabilities of mutating
ver 2.7.1 fixed for SKETCHBOOK and symmetrics
ver 2.7.2 better for SKETCHBOOK
ver 2.7.3 if genScore>=Score() then badGen=badGen+1 else badGen=0 end -- bug fixed thanks to robgee
ver 2.8 optional separate weight for each filter
ver 2.8.1 small changes
ver 2.9 non bonding mutate (for end game reducing BUNS)
EXPERIMENTAL weighted ScoreHerd() !! to be evaluated !
not "r" as HBOND candidate (with 3 bondable atoms, chances are low to make network)
ver 2.9.1 slall change adding FILTERMANAGE in Design titles
ver 2.9.3 fixed exploration filter
TO DO: complex score management
]]--
recipename= "Rav3n_pl GAB BIS Filter 2.9.3"
p=print
userbands=band.GetCount()
p("Starting "..recipename.. " on\n puzzle "..(puzzle.GetName()))
p(' with puzzle ID '..(puzzle.GetPuzzleID())..' and '..userbands..' user-supplied bands.\n ')
ssstr="" -- jeff101
gen=0
--SLOTS:
--[[
3 is best
4 used in recipe
5 used in recipe
50-100 used to record each big steps for movie at the end
]]--
SlotStepStart=50 -- New for SaveBigSteps()
SlotStep=SlotStepStart
-- START Wiggle Power Setting stuff
function DisplayLongWigglePower ()
local PowerSetting = behavior.GetWigglePower()
local result = ""
if PowerSetting == "l" then result = "Low"
elseif PowerSetting == "m" then result = "Medium"
elseif PowerSetting == "h" then result = "High"
elseif PowerSetting == "a" then result = "Auto"
end
return result
end
OriginalWPowerSetting= DisplayLongWigglePower ()
print(OriginalWPowerSetting .. " Wiggle Power")
function CheckWP() -- including Alarm Dialog (NOT USED YET)
if OriginalWPowerSetting == "Low" or OriginalWPowerSetting == "Auto" then
local ask=dialog.CreateDialog("Wiggle Power is ".. OriginalWPowerSetting)
ask.l1=dialog.AddLabel("Are you sure ?")
ask.continue=dialog.AddButton("Continue",1)
ask.stop=dialog.AddButton("Cancel",0)
askresult=dialog.Show(ask)
if askresult < 1 then return false end
end
return true
end
-- STOP Wiggle Power Setting stuff
undo.SetUndo(false)
OriginalFilterSetting = filter.AreAllEnabled()
NeverDisableSomeFilters= false -- used in FiltersOff
NeverEnableSomeFilters= false -- used in FiltersOn (no used actually)
AllFiltersTogether= false -- if true, it acceplerates FilersOn
PROBABLEFILTER= false
PROBABLEHBOND= false
GENERICFILTER=false
FILTERMANAGE= false
HASMUTABLE= false
IDEALCHECK= false
PROBABLESYM= false
SEPSIS= false
ELECTRON= false
CENTROID= false
HOTSPOT= false
EXPLORATION= false
SKETCHBOOK = false
conditions=false
sym=1
SYMMETRIC= false
HBONDING = false -- mutate only to bonding AAs
NOHBONDING = false -- mutate only to non bonding AAs
AlwaysUseLigand= false
useLigand= false
segCnt=structure.GetCount()
segCnt2=segCnt
while structure.GetSecondaryStructure(segCnt2)=="M" do segCnt2=segCnt2-1 end -- not used yet (ligand)
HASLIGAND=segCnt2<segCnt
startLigand=segCnt2+1
stopLigand=segCnt
SymCount=structure.GetSymCount() -- new 22/10/2019
if SymCount>0 then SYMMETRIC = true end
debugMode= false
function DEBUGprint(text2print)
local text2print = text2print or ""
if debugMode then
print(text2print)
end
end
--START extraction of information from puzzle metadata --Extrait des infos
function detectfilterandmut() -- Bruno Kestemont 10/10/2013; 13/2/2015; 5/1/2019; 1/3/2019
local descrTxt=puzzle.GetDescription()
local puzzletitle=puzzle.GetName()
local function SymetryFinder() -- by Bruno Kestemont 7/2/2013, 25/8/2013
local segMeanScore=(scoreboard.GetGroupScore()-8000)/segCnt -- top score pour eviter les debuts de puzzle
--p("Score moyen par segment= "..segMeanScore)
if PROBABLESYM then
if segMeanScore<33.39 then sym=1
PROBABLESYM=false
elseif segMeanScore<85 then sym=2
elseif segMeanScore<132 then sym=3
elseif segMeanScore<197 then sym=4
else sym=5
end
else sym=1
end
return
end
if #descrTxt>0 and (descrTxt:find("filter") or descrTxt:find("filters") or descrTxt:find("bonus") or descrTxt:find("bonuses") or descrTxt:find("Objectives") and not (descrTxt:find("disabled") or descrTxt:find("Sequence:")))then
PROBABLEFILTER=true -- WARNING ! There are false positives here !
FILTERMANAGE=true -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false -- to be evaluated
end
if #descrTxt>0 and (descrTxt:find("H-bond networks") or descrTxt:find("Hydrogen Bond Networks") or descrTxt:find("H-bond Networks")
or descrTxt:find("H-bond Network") or descrTxt:find("binding") and not descrTxt:find("disabled"))then
PROBABLEFILTER=true
FILTERMANAGE=false -- default no
GENERICFILTER=false -- to be evaluated
PROBABLEHBOND= true
HBONDING = true
end
if #descrTxt>0 and (descrTxt:find("design") or descrTxt:find("designs") or descrTxt:find("redesign")) then
HASMUTABLE=true
IDEALCHECK=true
HANDFOLD=true
PROBABLEFILTER=true -- WARNING ! There are false positives here !
end
if #descrTxt>0 and (descrTxt:find("De-novo") or descrTxt:find("de-novo") or descrTxt:find("freestyle")
or descrTxt:find("prediction") or descrTxt:find("predictions")) then
IDEALCHECK=true
HANDFOLD=true
PROBABLEFILTER=false
FILTERMANAGE=false
GENERICFILTER=false
end
if #descrTxt>0 and (descrTxt:find("Move Limit") or descrTxt:find("SKETCHBOOK") or descrTxt:find("sketchbook")) then
SKETCHBOOK=true
FILTERMANAGE=false
GENERICFILTER=false
print("SKETCHBOOK")
end
if #puzzletitle>0 then
if (puzzletitle:find("Sym") or puzzletitle:find("Symmetry") or puzzletitle:find("Symmetric")
or puzzletitle:find("Dimer") or puzzletitle:find("Trimer") or puzzletitle:find("Tetramer")
or puzzletitle:find("Pentamer")) then
PROBABLESYM=true
if puzzletitle:find("Dimer") and not puzzletitle:find("Dimer of Dimers") then sym=2
elseif puzzletitle:find("Trimer") or puzzletitle:find("trimer") then sym=3
elseif puzzletitle:find("Dimer of Dimers") or puzzletitle:find("Tetramer") then sym=4
elseif puzzletitle:find("Pentamer") then sym=5
else SymetryFinder()
end
end
if (puzzletitle:find("Design")) then
PROBABLEFILTER=true
end
end
if #descrTxt>0 and (descrTxt:find("Sym") or descrTxt:find("Symmetry") or descrTxt:find("Symmetric")
or descrTxt:find("sym") or descrTxt:find("symmetry") or descrTxt:find("symmetric")) then
PROBABLESYM=true
if (descrTxt:find("Dimer") or descrTxt:find("dimer") or descrTxt:find("C2 symmetry") or descrTxt:find("twoo symmetric"))
and not (descrTxt:find("Dimer of Dimers") or descrTxt:find("dimer of dimers")) then sym=2
elseif descrTxt:find("Trimer") or descrTxt:find("trimer") or descrTxt:find("C3 symmetry") or descrTxt:find("three symmetric") then sym=3
elseif (descrTxt:find("Dimer of Dimers") or descrTxt:find("Tetramer") or descrTxt:find("C4 symmetry") or descrTxt:find("four symmetric"))
and not (descrTxt:find("dimer of dimers") or descrTxt:find("tetramer"))then sym=4
elseif descrTxt:find("Pentamer") or descrTxt:find("pentamer") or descrTxt:find("C5 symmetry") or descrTxt:find("five symmetric") then sym=5
else SymetryFinder()
end
end
--print resulting sym info
if PROBABLESYM then
print("Symmetric")
if sym==2 then
print("Dimer")
elseif sym==3 then
print("Trimer")
elseif sym==4 then
print("Tetramer")
elseif sym==5 then
print("Pentamer")
elseif sym>5 then
print("Terrible polymer")
end
else print("Monomer")
end
if #puzzletitle>0 and puzzletitle:find("Sepsis") then -- new BK 17/6/2013
SEPSIS=true
HANDFOLD=true
--p(true,"-Sepsis")
print("Sepsis")
end
if #puzzletitle>0 and puzzletitle:find("Design") then -- new BK 11/9/2020
PROBABLEFILTER=true -- WARNING ! There are false positives here !
FILTERMANAGE=true -- default yes during wiggle (will always be activate when scoring)
GENERICFILTER=false -- to be evaluated
end
if #puzzletitle>0 and puzzletitle:find("Electron Density") then -- for Electron Density
--p(true,"-Electron Density")
ELECTRON=true
HANDFOLD=true
print("Electron density")
end
if #puzzletitle>0 and puzzletitle:find("Centroid") then -- New BK 20/10/2013
--p(true,"-Centroid")
CENTROID=true
print("Centroid")
end
if #puzzletitle>0 and puzzletitle:find("Hotspot") then -- New BK 21/01/2014
HOTSPOT=true
print("Hotspot")
end
return
end
detectfilterandmut()
--END extraction of information from puzzle metadata --Extrait des infos
--START Generic Filter Management by BitSpawn 21/12/2014
--Source: http://fold.it/portal/node/1998917
-- function to copy class/table
-- Handy shorts module
function CopyTable(orig)
local copy = {}
for orig_key, orig_value in pairs(orig) do
copy[orig_key] = orig_value
end
return copy
end
-- function to overload a funtion
function mutFunction(func)
local currentfunc = func
local function mutate(func, newfunc)
local lastfunc = currentfunc
currentfunc = function(...) return newfunc(lastfunc, ...) end
end
local wrapper = function(...) return currentfunc(...) end
return wrapper, mutate
end
-- function to overload a class
-- to do: set the name of function
classes_copied = 0
myclcp = {}
function MutClass(cl, filters)
classes_copied = classes_copied+1
myclcp[classes_copied] = CopyTable(cl)
local mycl =myclcp[classes_copied]
for orig_key, orig_value in pairs(cl) do
myfunc, mutate = mutFunction(mycl[orig_key])
if filters==true then
mutate(myfunc, function(...)
FiltersOn()
if table.getn(arg)>1 then
-- first arg is self (function pointer), we pack from second argument
local arguments = {}
for i=2,table.getn(arg) do
arguments[i-1]=arg[i]
end
return mycl[orig_key](unpack(arguments))
else
--print("No arguments")
return mycl[orig_key]()
end
end)
cl[orig_key] = myfunc
else
mutate(myfunc, function(...)
FiltersOff()
if table.getn(arg)>1 then
local arguments = {}
for i=2, table.getn(arg) do
arguments[i-1]=arg[i]
end
return mycl[orig_key](unpack(arguments))
else
return mycl[orig_key]()
end
end)
cl[orig_key] = myfunc
end
end
end
-- how to use:
--setting default options if filters BK 4/2/2015
--MutClass(structure, false)
--MutClass(band, false)
--MutClass(current, true)
function GenericFilterMngt()
if GENERICFILTER then -- WARNING: TO VERIFY !! (may be it's irreversible for several funtions bellow)
MutClass(structure, false)
MutClass(band, true) -- must be true to avoid unideal loop bug
MutClass(current, true)
MutClass(recentbest, true) -- otherwise, it remembers cut solutions
MutClass(save, true) -- better to save with full score
end
end
--STOP Generic Filter Management
-- functions for filters
--[[
if PROBABLEFILTER then -- new functions 17/7/2018
filter.GetNames() -- Get all filter names
filter.GetEnabledNames() -- Get active filter names
filter.GetDisabledNames() -- Get disabled filter names
filter.AreAllEnabled() -- True if all filters are active
filter.Enable(string) -- Enables the named filter
filter.Disable(string) -- Disables the named filter
filter.EnableAll() -- Enables all filters
filter.DisableAll() -- Disables all filters
filter.GetBonusTotal() -- Total bonus score of the filters
filter.GetBonus(string) -- Bonus score of the named filter
filter.ConditionSatisfied(string) -- Is the condition satisfied or the filter active
filter.IsEnabled(string) -- Is the filter Enabled
end
]]--
function return2GlobalOriginalFilterSetting()
if OriginalFilterSetting then -- if true, all filters are default enabled
filter.EnableAll() --Enables all filters
else
filter.DisableAll() --Disables all filters
end
end
function return2IndividualOriginalFilterSetting() -- returns to original choice from dialog
--DEBUGprint("Return to Original Filters")
--InitialFilters -- {stringfilter, FilterBonus, FilterEnabled, FilterCondition, ThisFilterWeight}
for i = 1, #InitialFilters do
if InitialFilters[i][3] then filter.Enable(InitialFilters[i][1]) else filter.Disable(InitialFilters[i][1]) end
--DEBUGprint("Is ".. InitialFilters[i][1].." active ? "..tostring(filter.IsEnabled(InitialFilters[i][1])))
end
end
function return2OriginalFilterSetting()
if AllFiltersTogether then
return2GlobalOriginalFilterSetting()
else
return2IndividualOriginalFilterSetting()
end
end
function FiltersOn() -- all of them WARNING New version 20/2/2019, needs NeverEnableSomeFilters
if NeverEnableSomeFilters then
return2OriginalFilterSetting()
else
if not filter.AreAllEnabled() then -- if not all filters are enabled, enable all of them
filter.EnableAll() -- Enables all filters
end
end
end
function FiltersOff() -- WARNING new version 20/2/2019, needs NeverDisableSomeFilters
if NeverDisableSomeFilters then
return2OriginalFilterSetting()
--if filter.AreAllEnabled() then -- only if all filters are enabled, disable all of them. If only some of them are enabled, do nothing
-- filter.DisableAll() -- Disables all filters
--end
else
filter.DisableAll() -- Disables all filters
end
end
--STOP functions for filters
--START SET NOTES BY PD
--[[ Set note with author, recipe and big steps -- By Pauldunn
This subroutine is used in much group recipe versions in various groups
originally in Go Science
It traces the various recipes and players that improved the current solution
Module to quick copy-paste it in existing recipes, by Bruno Kestemont
]]--
--HOW TO include in a recipe?
--1a) COPY from here ================
function InitNotes(recipename)
local recipename=recipename or ""
note_number=structure.GetCount()
for seg=structure.GetCount(),1,-1 do
if structure.GetNote(seg)~="" then break end
note_number=seg
end
print(string.format("Recording "..recipename.." results in Note for segment %i",note_number))
starting_score=current.GetScore()
--structure.SetNote(note_number,string.format("(%s) %.3f + %s(%i) %.3f",user.GetPlayerName(),starting_score,recipename,loop_count,current.GetScore()))
end
function SetNote(note_number, starting_score, recipename, loop_count)
local recipename=recipename or ""
local starting_score=starting_score or 0
local loop_count=loop_count or 1
--print(string.format("Recording "..recipename.." results in Note for segment %i",note_number))
structure.SetNote(note_number,string.format("(%s) %.3f + %s(%i) %.3f",user.GetPlayerName(),starting_score,recipename,loop_count,current.GetScore()))
end
InitNotes(recipename) -- WARNING: and match "recipename" with the recipename of the recipe
--1b) COPY to here. somewhere after the options and inits of the recipe ================
--3a) Uncommend the 2 following lines and COPY from here ---------------
--loop_count=gen -- adapt "gen" to the name of loops used in the recipe
--SetNote(note_number, starting_score, recipename, loop_count) -- WARNING: and mach "recipename" and "loop_count" with the names of the recipe
--3b) COPY to here after main generations or loops of the recipe --------------
--END SET NOTES BY PD
-- options:
energy=false --set true to seek energy in exploration puzzles; false works on all puzzles
pullCI=0.9 --Clash Impotrance during pull
shakeCI=0.21 --CI when shake on qstab or mutate once
maxCI=1 --maximum CI used by script
fastQstab=true -- only 1s1w after pull if true
fuzeThresh = 1 -- run fuze if we are close to best score (negative=new best score)
qstabThresh=1 -- run qstab if score drops more than... wiggle only in other case
onlyMutable=true --if true use ONLY all mutable aas, no matter of always use
mutateOnce=false --if true use mutate(1) instead of shake in qstab
mutateAlwas=false --if true use muatate(1) instead of all shakes
minGain= 0 -- minimum gain accepted per iteration (for sketchbook puzzles)
MaxLossIFG= 0 -- default maximum loss accepted if filter gains
MaxMaxLossIFG=100 -- maximum maximum loss accepted if filter gains (for dialog)
herd= --herd options
{
breedBest = 5, --breed best 4 critters - all combinations => 6 kids form 4 critters, 3 form 3, 1 form 2, 9 form 5 ;]
keepBest = 3, --save up to 3 best scoring critters, rest are forgotten
breedBastards = 8, --number of best will have one random bastard
newRandom = 10, --adding new random ones each generation
maxGen= 1000, --maximum generations
shuffle = true, --set true to run critters in random order
renew=4, --create totally fresh 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. Basttards always breed
maxLoss=30, --maximum loss by critter. set 0 to disable
}
bands= --bands options
{
minStr=0.3, --minimum band str
maxStr=1.1, --maximum band str
maxUp = 12.1, -- maximum band lenght
minUp = 0.1 -- debugging case=0
}
DoNotUse={--just comment lines below or add more areas to avoid
--{segCnt,segCnt}, --ligand cant be used
--{120,134},
--{1,10},
}
AlwaysUse={ --areas should be always used
--{238,238}--ligand need to be at one end
--{308,311}, --loopy
--{272,319}, --loopy
}
UseSegments= --use ONLY this segments
{
--2,3,4,5
}
-- 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
}
--end of options
if HASLIGAND then useLigand=true end
if useLigand==false then
segCnt=segCnt2
end
function DoAlwaysUseLigand()
if HASLIGAND and AlwaysUseLigand then
AlwaysUse={ --areas should be always used
{startLigand,stopLigand}--ligand need to be at one end
--{308,311}, --loopy
--{272,319}, --loopy
}
end
end
DoAlwaysUseLigand()
--START quick save algorithm
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 )
--print ( 'Done.' )
end
--END quick save algorithm
function CI(c)
if c>maxCI then c=maxCI end
behavior.SetClashImportance(c)
end
function round(x)--cut all afer 3-rd place
return x-x%0.001
end
function down(x)
return x-x%1
end
--------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
--START Filter subroutine
--Test of the new filter functions
--[[
filter.GetNames() --Get all filter names
filter.GetEnabledNames() --Get active filter names
filter.GetDisabledNames() --Get disabled filter names
filter.AreAllEnabled() --True if all filters are active
filter.Enable(string) --Enables the named filter
filter.Disable(string) --Disables the named filter
filter.EnableAll() --Enables all filters
filter.DisableAll() --Disables all filters
filter.GetBonusTotal() --Total bonus score of the filters
filter.GetBonus(string) --Bonus score of the named filter
filter.ConditionSatisfied(string) --Is the condition satisfied or the filter active
filter.IsEnabled(string) --Is the filter Enabled
puzzle.GetStructureName() --Get the structure name of (multistart) puzzle
save.LoadSolutionByName(string) --Load the highest scoring solution by name
save.SaveSolution(string) --Save a solution
SetSecondaryStructure (and selected variant) --now can specify 'a' to auto-assign structure by DSSP
--]]
--recipename="Filters"
print (puzzle.GetStructureName())
FilterList={}
FilterTable={} -- {filter string, bonus, enabled, condition}
FilterEnabled={}
FilterDisabled={}
AreAllFiltersEnabled=filter.AreAllEnabled()
-- Segment set and list module
-- Notice that most functions assume that the sets are well formed
-- (=ordered and no overlaps)
-- 02-05-2012 TvdL Free to use for non commercial purposes
-- 05-02-2014 BK enhanced (also Free for non commercial purposes)
-- a list is {1,2,3,8,9,10}. A set is {{1,4},{8,10}} or something like that in a 2-dimentional table:
--i {[1],[2]}
--___________
--1 { 1 , 4 }
--2 { 8 , 10}
function SegmentExtendSet(set,x,y) -- New BK 05/02/2014 x, y= nb of seg to add before and after each zone
local result={}
local x,y=x or 1, y or 1
for i=1,#set do
if set[i][1]<1+x then --first segment
result[i]={set[i][1],set[i][2]+y}
elseif set[i][2]>set[#set][2]-y then --last segment
result[i]={set[i][1]-x,set[i][2]}
else
result[i]={(set[i][1]-x and set[i][1]>x) or set[i][1] , (set[i][2]<=set[#set][2]-y and set[i][2]+y) or set[i][2]}
result[i]={set[i][1]-x ,set[i][2]+y}
end
end
return result -- overlaps are possible
end
function SegmentListToSet(list) --note: clean duplicates
local result={}
local f=0
local l=-1
table.sort(list)
for i=1,#list do
if list[i] ~= l+1 and list[i] ~= l then
-- note: duplicates are removed
if l>0 then result[#result+1]={f,l} end
f=list[i]
end
l=list[i]
end
if l>0 then result[#result+1]={f,l} end
--print("list to set")
--SegmentPrintSet(result)
return result
end
function SegmentSetToList(set)
local result={}
for i=1,#set do
--print(set[i][1],set[i][2])
for k=set[i][1],set[i][2] do
result[#result+1]=k
end
end
return result
end
function SegmentCleanSet(set) -- clean duplicates
-- Makes it well formed and clean duplicates
return SegmentListToSet(SegmentSetToList(set))
end
function SegmentInvertSet(set,maxseg)
-- Gives back all segments not in the set
-- maxseg is added for ligand
local result={}
if maxseg==nil then maxseg=structure.GetCount() end
if #set==0 then return {{1,maxseg}} end
if set[1][1] ~= 1 then result[1]={1,set[1][1]-1} end
for i=2,#set do
result[#result+1]={set[i-1][2]+1,set[i][1]-1}
end
if set[#set][2] ~= maxseg then result[#result+1]={set[#set][2]+1,maxseg} end
return result
end
function SegmentInList(s,list)
table.sort(list)
for i=1,#list do
if list[i]==s then return true
elseif list[i]>s then return false
end
end
return false
end
function SegmentInSet(set,s)
for i=1,#set do
if s>=set[i][1] and s<=set[i][2] then return true
elseif s<set[i][1] then return false
end
end
return false
end
function SegmentJoinList(list1,list2) -- duplicates allowed
local result=list1
if result == nil then return list2 end
for i=1,#list2 do result[#result+1]=list2[i] end
table.sort(result)
return result
end
function SegmentJoinSet(set1,set2) -- no duplicates, no overlap
return SegmentListToSet(SegmentJoinList(SegmentSetToList(set1),SegmentSetToList(set2)))
end
function SegmentCommList(list1,list2) -- what is common to 2 lists
local result={}
table.sort(list1)
table.sort(list2)
if #list2==0 then return result end
local j=1
for i=1,#list1 do
while list2[j]<list1[i] do
j=j+1
if j>#list2 then return result end
end
if list1[i]==list2[j] then result[#result+1]=list1[i] end
end
return result
end
function SegmentCommSet(set1,set2) -- what is common to set 1 and set 2 (no duplicate)
return SegmentListToSet(SegmentCommList(SegmentSetToList(set1),SegmentSetToList(set2)))
end
function SegmentSetMinus(set1,set2) -- set1 minus set2 (set1 minus what is common to set 1 and set2)(no duplicate)
return SegmentCommSet(set1,SegmentInvertSet(set2))
end
function SegmentPrintSet(set)
print(SegmentSetToString(set))
end
function SegmentSetToString(set)
local line = ""
for i=1,#set do
if i~=1 then line=line..", " end
line=line..set[i][1].."-"..set[i][2]
end
return line
end
function SegmentSetInSet(set,sub)
if sub==nil then return true end
-- Checks if sub is a proper subset of set
for i=1,#sub do
if not SegmentRangeInSet(set,sub[i]) then return false end
end
return true
end
function SegmentRangeInSet(set,range)
if range==nil or #range==0 then return true end
local b=range[1]
local e=range[2]
for i=1,#set do
if b>=set[i][1] and b<=set[i][2] then
return (e<=set[i][2])
elseif e<=set[i][1] then return false end
end
return false
end
function SegmentSetToBool(set)
local result={}
for i=1,structure.GetCount() do
result[i]=SegmentInSet(set,i)
end
return result
end
helptext="" -- for dialog
--WARNING: no boolean allowed in matrix bellow (printintg boolean results in error).
-- if you need to print booleans, first change your matrix to text matrix
function SegmentMatrixToString(matrix, dimension, fieldD, field, recorD, record, printN) -- New BK 14/10/2014, 06/03/2019 tostring added
local dimension=dimension or 1 -- the dimension of the matrix must be specified if not 1 (simple list)
local fieldD=fieldD or ";" -- field delimiter for print
local field=field or 1 -- if we decide only to print one field (field number)
local recorD=recorD or "," -- record delimiter for print
local record=record or nil -- if nil, we print all records
local printN=printN or false
local line = ""
local matrix= matrix or {}
if #matrix>0 then
if record== nil then
for i=1,#matrix do
if i~=1 then line=line..recorD end -- add record delimiter unless for first record
if printN then line=line.." "..i..") " end
for j=1,dimension do
line=line..tostring(matrix[i][j]) -- add data
if j~=dimension then line=line..fieldD end -- add field delimiter unless for latest field
end
end
else
if printN then line=line..record..") " end
for j=1,dimension do
line=line..tostring(matrix[record][j]) -- add data
if j~=dimension then line=line..fieldD end -- add field delimiter unless for latest field
end
end
helptext=""
helpdesk2=[[]]
return line
else
helptext=""
return ""
end
end -- this function can only report a line
function SegmentPrintMatrix(matrix,dimension, fieldD, field, recorD, record, printN, display) -- New BK 14/10/2014
local matrix=matrix or {}
local dimension=dimension or 1 -- the dimension of the matrix must be specified if not 1 (simple list)
local fieldD=fieldD or ";" -- field delimiter for print
local field=field or 1 -- if we decide only to print one field (field number)
local recorD=recorD or "," -- record delimiter for print
local record=record -- if nil, we print all records, or we print the record i
local printN=printN or false
local display=display or "list" -- can be "table" or "list"
if display=="list" then -- prints all in one line
print(SegmentMatrixToString(matrix,dimension, fieldD, field, recorD, record, printN))
elseif display=="table" then
for i=1,#matrix do
print(SegmentMatrixToString(matrix,dimension, fieldD, field, recorD, i, printN))
end
end
end
--- End of Segment Set module
---START Get and print filter list
function GetFilterList(printAll) -- returns a FilterTable with {stringfilter, FilterBonus, FilterEnabled, FilterCondition, ThisFilterWeight}
local printAll= printAll or false
local FilterTableResult={}
local FilterBonus=0
FilterList=filter.GetNames()
if printAll then
print(#FilterList.." filters. Name, bonus, enabled, condition, weight:")
end
print("DEBUG1")
for i= 1, #FilterList do -- {filter string, bonus, enabled, condition}
local stringfilter=FilterList[i] -- string
print("DEBUG1"..stringfilter)
if stringfilter=="Move Count Limit" or stringfilter=="Exploration" then FilterBonus=0 else
FilterBonus=filter.GetBonus(stringfilter) -- number
end
local FilterEnabled= filter.IsEnabled(stringfilter) -- BOOLEAN Is the filter Enabled
local FilterCondition= filter.ConditionSatisfied(stringfilter) -- BOOLEAN Is the condition satisfied or the filter active
local ThisFilterWeight=1 -- the specific weight for each filter
table.insert(FilterTableResult,{stringfilter, FilterBonus, FilterEnabled, FilterCondition, ThisFilterWeight}) -- TO DO: keep and compare the best ones
if printAll then
print(FilterList[i].." : "..FilterBonus.." Enabld: "..tostring(FilterEnabled).." Met= "..tostring(FilterCondition).." Wght= "..ThisFilterWeight)
end
end
return FilterTableResult -- returns a FilterTable with {filter string, bonus, enabled, condition, weight}
end
function printFilterScores(long) -- calculates a new global FilterTable as well
local long= long or false
FilterTable=GetFilterList(long) -- {stringfilter, FilterBonus, FilterEnabled, FilterCondition, ThisFilterWeight}
TotalBonus=filter.GetBonusTotal() -- only used in FilersOK
if long then print("Total Bonus= "..TotalBonus) end
BestTotalBonus=TotalBonus -- init (only used in FilersOK)
matrix=FilterTable
dimension= 2 -- actually the number of colomns we'll print for a table
fieldD=": +"
recordD=","
field=nil -- we don't select only one column
record=nil -- we don't select only one line
printN=true -- print the number of records
--display= "list" -- all in one line
display= "table" -- one record after the other (a table print)
--SegmentPrintMatrix(matrix,dimension, fieldD, field, recorD, record, printN, display)
end
printFilterScores(true) -- inits to create reference FilterTable, empty if false, and TotalBonus
---END Get and print filter list
---START select Filters
FilterWeight=1
EQUALIZED= false
function GetParametersFilters () -- AARGH I don't succeed in checking the boxes with right default DialogFilter[i][3]!!! 06/03/2019
local dlogresult=9
local dlog = dialog.CreateDialog ( recipename )
local DialogFilter=FilterTable -- {stringfilter, FilterBonus, FilterEnabled, FilterCondition, ThisFilterWeight}
local CountCheckedOnes = 0
repeat
dlog.L1=dialog.AddLabel("Filters weighting:")
DEBUGprint("Equalized = ".. tostring(EQUALIZED))
for i = 1, #DialogFilter do
DEBUGprint("Dialog Init "..DialogFilter[i][1]..": ".. tostring(DialogFilter[i][3])..": ".. tostring(DialogFilter[i][4])..DialogFilter[i][5])
dlog[DialogFilter[i][1]]=dialog.AddCheckbox(DialogFilter[i][1], DialogFilter[i][3]) -- WARNING DialogFilter[i][1] must be a string ! don't use DialogFilter[i][3] here
end -- {stringfilter, FilterBonus, FilterEnabled, FilterCondition, ThisFilterWeight}
dlog.L2=dialog.AddLabel("------------")
dlog.NeverDisableSomeFilters=dialog.AddCheckbox("Never disable selected ones", NeverDisableSomeFilters)
dlog.NeverEnableSomeFilters=dialog.AddCheckbox("Never enable unselected ones (!?)", NeverEnableSomeFilters)
dlog.L3=dialog.AddLabel("------------")
dlog.L4=dialog.AddLabel("Priority to score or filter:")
if not EQUALIZED then
dlog.FilterWeight=dialog.AddSlider("Filters multiplyer", FilterWeight, 1, 100,0)
end
dlog.MaxLossIFG=dialog.AddSlider("Max score loss", MaxLossIFG, 0, MaxMaxLossIFG,0)
dlog.OK = dialog.AddButton ( "OK" , 1 )
dlog.more= dialog.AddButton ( "Equalizer" , 2 )
dlog.Cancel = dialog.AddButton ( "Cancel" , 0 )
dlogresult = dialog.Show ( dlog ) -- BUG "VALUE MUST BE A STRING" IF see WARNING above !
if dlogresult > 0 then
CountCheckedOnes = 0
for i = 1, #DialogFilter do
DialogFilter[i][3]=dlog[DialogFilter[i][1]].value
if DialogFilter[i][3] then filter.Enable(DialogFilter[i][1]) CountCheckedOnes=CountCheckedOnes+1 else filter.Disable(DialogFilter[i][1]) end
DEBUGprint(DialogFilter[i][1], tostring(DialogFilter[i][3]))
end
NeverDisableSomeFilters=dlog.NeverDisableSomeFilters.value
NeverEnableSomeFilters=dlog.NeverEnableSomeFilters.value
MaxLossIFG=dlog.MaxLossIFG.value
if not EQUALIZED then -- it doesn't work !
FilterWeight=dlog.FilterWeight.value
end -- else 1 (no doubble weight)
if CountCheckedOnes==#DialogFilter then AllFiltersTogether= true end
end
if dlogresult==2 then DialogFilter=GetParametersEqualizer (DialogFilter) end -- then return to here
until dlogresult<2 -- do not end if result is anything but Cancel
return DialogFilter
end -- {stringfilter, FilterBonus, FilterEnabled, FilterCondition, ThisFilterWeight}
function GetParametersEqualizer (DialogFilter) -- AARGH I don't succeed in checking the boxes with right default DialogFilter[i][3]!!! 06/03/2019
local dlogresult=9
local dlog = dialog.CreateDialog ( recipename )
local DialogFilter= DialogFilter
repeat
dlog.L1=dialog.AddLabel("Filters weighting:")
DEBUGprint("")
for i = 1, #DialogFilter do
DEBUGprint("Dialog Init "..DialogFilter[i][1]..": ".. tostring(DialogFilter[i][3])..": ".. tostring(DialogFilter[i][4])..DialogFilter[i][5])
dlog[DialogFilter[i][1]]=dialog.AddSlider((DialogFilter[i][1]), DialogFilter[i][5],0,10,1)
end -- {stringfilter, FilterBonus, FilterEnabled, FilterCondition, ThisFilterWeight}
dlog.OK = dialog.AddButton ( "OK" , 1 )
dlogresult = dialog.Show ( dlog ) -- BUG "VALUE MUST BE A STRING" IF see WARNING above !
if dlogresult > 0 then
for i = 1, #DialogFilter do
DialogFilter[i][5]=dlog[DialogFilter[i][1]].value
DEBUGprint(DialogFilter[i][1], DialogFilter[i][5])
if DialogFilter[i][5] ~= 1 then EQUALIZED = true end -- it doesn't work !
end
end
until dlogresult<2
return DialogFilter-- with weighting values added
end -- {stringfilter, FilterBonus, FilterEnabled, FilterCondition, ThisFilterWeight}
function SelectAndPrintFilters()
InitialFilters=GetParametersFilters () -- {stringfilter, FilterBonus, FilterEnabled, FilterCondition, ThisFilterWeight}
BestFilters=InitialFilters -- {stringfilter, FilterBonus, FilterEnabled, FilterCondition, ThisFilterWeight}
SegmentPrintMatrix(InitialFilters,4, ": +", nil, ",", nil, true, "table")
end
if PROBABLEFILTER then
SelectAndPrintFilters() -- initializes the filter matrix to InitialFilters and BestFilters as well as printing it
end
---END select Filters
--END FILTER SUBROUTINE
--------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
function Score(filterstoo)--return score, exploration too, filter weighted score if true. WARNING
local filterstoo = filterstoo or false-- WARNING, filters are nul if disabled !(weight cannot work on disabled)
local result=0
local result0=result
local filters = 0
local FilterList=FilterList
if PROBABLEFILTER and filterstoo then
filters=filter.GetBonusTotal()
end
if energy==true then
result= current.GetEnergyScore()
else
result= current.GetScore()
end
result0=result
if EQUALIZED and filterstoo then
for i= 1, #FilterList do
local FilterBonus=0
local stringfilter=FilterList[i]
-- bonus x user weight for each filter
if stringfilter=="Move Count Limit" or stringfilter=="Exploration" then FilterBonus=0 else
FilterBonus=filter.GetBonus(stringfilter) -- number
end
local ThisFilterWeight=InitialFilters[i][5]
--if FilterBonus==0 and ThisFilterWeight~= 1 then FilterBonus=1 end -- in order to give a weight to a non rewarding 0 filter
result=result+FilterBonus * ThisFilterWeight
DEBUGprint(FilterList[i]..": "..FilterWeight.." x "..FilterBonus.." x "..ThisFilterWeight)
end
end
if FilterWeight > 1 then -- global weight only, or extra-weight !!!
result= result + filters * (FilterWeight-1) -- only normal result with or without filters if not filterstoo
end
DEBUGprint("Score: "..result0.." Weighted: "..result)
DEBUGprint ("Difference due to weights: ".. result-result0)
return result
end
function FiltersOK() -- jump Qstab if total filter decreased
if not PROBABLEFILTER then return true end -- otherwise we have a bug
FiltersOn()
local TotalBonus=filter.GetBonusTotal()
local fg=TotalBonus-BestTotalBonus
return2OriginalFilterSetting()
if fg<0 then return false -- DEBUG
else
BestTotalBonus=TotalBonus
return true
end
end
bestScoreW=Score(true) -- INIT global score with overweight filters
--local seed=recipe.GetRandomSeed() --NOT WORKING!!!
--calculate REALLY good seed
seed=os.time()/math.abs(Score())
seed=seed%0.001
seed=1/seed
while seed<10000000 do seed=seed*1000 end
seed=seed-seed%1
p("Seed is: "..seed)
math.randomseed(seed)
--REALLY good seed made by rav3n_pl :P
CandidateHB= {} -- in order to limit mutate (of banding) to segments that are in the core of the complex sym.
HbondMaxDistance= 16 -- its for symmetrics when branches are far away (seg to other seg), set 4 for seg to seg
function getShortDistSym() -- 1 dim for speed, only the shorter distance is calculated (between same segments)
if not SYMMETRIC then return end
local iBand= 0
local distance= 0
HbondMaxDistance= 16 -- reinitializing at each step
print("Calculating short distances to symmetrics... be patient it's SLOW !")
local s= 1 -- it's not usefull to pass all symmetrics, distances are equal
CandidateHB[0]= false
for i=1,segCnt do --filling table for syms, only banding between same segments is usefull here (these are the smalest distances)
iBand=band.AddBetweenSegments(i,i,0,0, s) -- adding a band to symmetrics for measuring distance
-- (integer segmentIndex1, integer segmentIndex2, [integer atomIndex1], [integer atomIndex2], [integer symnr2])
distance=band.GetLength(iBand)
if distance < HbondMaxDistance then
HbondMaxDistance=distance + 1
if HbondMaxDistance < 7 then HbondMaxDistance = 7 end -- the case when symmetry is very close, "ideal"
end
band.Delete(iBand)
if distance < HbondMaxDistance and distance > 3 then -- Hbond between end atom is 1.41
CandidateHB[i]= true -- seg i is a candidate for HB, it can be candidate because of sym 1 or sym 2 etc.
DEBUGprint("seg".. i.." distance:"..distance)
end
end
print("Done. Distance between syms: ".. HbondMaxDistance)
end -- at the end, we have a list of bondable segments "if CandidateHB[x] then mutate to charged"
function ProbabilityMutating(iter) -- favour certain AAs (e.g. charged ones for hbond puzzles), here respecting hydrophobicity by default
local iter=iter or 1
DEBUGprint("Probability mutating")
if HBONDING then
for i = 1, segCnt do
DEBUGprint("Mutating seg"..i)
if selection.IsSelected(i) and CandidateHB[i] then
recentbest.Save() -- TO DO : quicksaves because recentbest is already used in fuse !!
local SS=structure.GetSecondaryStructure(i)
local Hydrophobic= structure.IsHydrophobic(i)
local beforeAA= structure.GetAminoAcid(i)
local list = {} -- {'s','t','n','q','r','h','k','d','e','w','y'} -- charged
if SS == "L" and Hydrophobic then list={} end
if SS == "L" and not Hydrophobic then list={'s','n','h','d'} end -- h is a waste of time
if SS == "E" and Hydrophobic then list={'y'} end -- {'w','y'} these are too big, it's a waste of time
if SS == "E" and not Hydrophobic then list={'t'} end -- a waste of time
if SS == "H" and Hydrophobic then list={} end
if SS == "H" and not Hydrophobic then list={'q','k','e'} end -- helix phillix, unless "r" with 3 bonding atoms. k is long.
for j =1, #list do
structure.SetAminoAcid(i, list[j])
end
recentbest.Restore() -- no shake etc, it's mimimalist !
local afterAA=structure.GetAminoAcid(i)
if beforeAA ~= afterAA then
print("Mutated seg"..i..": "..beforeAA.."->"..structure.GetAminoAcid(i))
end
end
end
elseif NOHBONDING then
for i = 1, segCnt do
DEBUGprint("Mutating seg"..i)
if selection.IsSelected(i) and CandidateHB[i] then
recentbest.Save() -- TO DO : quicksaves because recentbest is already used in fuse !!
local SS=structure.GetSecondaryStructure(i)
local Hydrophobic= structure.IsHydrophobic(i)
local beforeAA= structure.GetAminoAcid(i)
local list = {} -- aaneutral= {'v','l','m','p','i','a','c','f','g'} -- non H binding
if SS == "L" and Hydrophobic then list={'g','p'} end --aaloopphobic={'g','p'}
if SS == "L" and not Hydrophobic then list={} end --aaloopphilic={'d','h','n','s'}
if SS == "E" and Hydrophobic then list={'c','f','i','v'} end--aasheetphobic={'c','f','i','v','w','y'}
if SS == "E" and not Hydrophobic then list={} end --aasheetphilic={'t'}
if SS == "H" and Hydrophobic then list={'a','l','m'} end --aahelixphobic={'a','l','m'}
if SS == "H" and not Hydrophobic then list={} end aahelixphilic={'e','k','q','r'}
for j =1, #list do
structure.SetAminoAcid(i, list[j])
end
recentbest.Restore() -- no shake etc, it's mimimalist !
local afterAA=structure.GetAminoAcid(i)
if beforeAA ~= afterAA then
print("Mutated seg"..i..": "..beforeAA.."->"..structure.GetAminoAcid(i))
end
end
end
else
structure.MutateSidechainsSelected(iter)
end
end
function Wiggle(how, iters, minppi, valit, mingps) -- NEW one, with filter management, 17/4/2016, SKETCHBOOK adaped 24/2/2020
if FILTERMANAGE then FiltersOff() end-- new BK 8/4/2013, always disable filter here. Keep the few ones active if not all selected in dialog.
if how==nil then how="wa" end --WARNING: on filter disabled, weight doesn't work !!
if iters==nil then iters=6 end
if minppi==nil then minppi=0.1 end
if valit==nil then valit= 1 end
if SKETCHBOOK then valit = 30 end
if mingps==nil then mingps= 80 end
if iters>0 then
startRecTime=os.clock ()
iters=iters-1
sp=Score(true) -- weighted
if how == "s" then
if FILTERMANAGE then return2OriginalFilterSetting() end -- new BK 8/4/2013, always back to user settings for s or m
if mutateAlwas==true then if SKETCHBOOK then structure.MutateSidechainsSelected(3) else ProbabilityMutating(1) end
--else structure.ShakeSidechainsSelected(1) end
else if SKETCHBOOK then structure.ShakeSidechainsSelected(3) else QuickShake() end end
elseif how == "wb" then structure.WiggleAll(valit, true,false)
elseif how == "ws" then structure.WiggleAll(valit, false,true)
elseif how == "wa" then structure.WiggleAll(valit)
end
ep = Score(true) -- weighted
ig=ep-sp
timeperiteration =os.clock ()-startRecTime
tppii=ig/timeperiteration
--print("g/s: "..ig.." / "..timeperiteration.." = "..tppii)
if how~="s" then
if ig > minppi and tppii > mingps then return Wiggle(how, iters, minppi, valit) end --tail call
end
end
if FILTERMANAGE then return2OriginalFilterSetting() end -- new BK 10/10/2013, always back to user settings
end
function SaveBest() -- NEW version with overweight of filters 20/2/2019
local ScoreU=Score()
local ScoreW=Score(true)
local g=ScoreU-bestScore -- with or without unweighted filters
local gW=ScoreW-bestScoreW
if not (conditions and not current.AreConditionsMet()) and gW>=0 then --NEW, we don't keep if weighted filters decrease
if g>0 + minGain - MaxLossIFG then -- Note: better not using mingain and MaxLossIFG together
if g>0.01 then p("Gained another "..round(g).." pts.") end
bestScore=ScoreU -- global score with or without normal filters
bestScoreW=ScoreW --global score with overweight filters
save.Quicksave(3)
if g<0 then print("Cost of a bonus gain: ".. round(g).." pts. New score: "..round(bestScore)) end
end
else
print("Conditions not met. Returning to former solution") -- for DEBUG
end
end
function SaveRB()
if recentbest.GetScore()>bestScore or (recentbest.GetEnergyScore()>bestScore and energy==true)then
if not (conditions and not current.AreConditionsMet()) then
save.Quicksave(4)
recentbest.Restore()
SaveBest()
save.Quickload(4)
end
end
end
function Qstab()
selection.SelectAll()
CI(shakeCI)
if mutateOnce==true then
--structure.MutateSidechainsSelected(1)
ProbabilityMutating(1)
else
Wiggle("s",1)
end
if fastQstab==false then
CI(0.4)
Wiggle("wa",1)
CI(1)
Wiggle("s",1)
end
CI(1)
Wiggle()
end
function FuzeEnd()
CI(1)
Wiggle("wa",1)
Wiggle("s",1)
Wiggle()
srb()
end
function Fuze1(ci1,ci2)
CI(ci1)
Wiggle("s",1)
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()
p("Fuzing...")
local scr=Score()
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 random(n1,n2) --random function returns int or float depends on input vars
if n1==nil then return math.random()
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 FillHerd() --fill up herd
local n=#critters
if n>0 then --fill up
n=herd.newRandom
else --fresh herd
n=herd.breedBest + herd.keepBest + herd.breedBastards
end
p("Randomizing "..n.." new critters...")
for i=1,n do
AddCritter()
end
end
function AddCritter() --vreate new random critter
local c={}
critterID=critterID+1
c.no=critterID
c.name=c.no..'-rnd'
c.bands={}
local r=random(critter.minBands, critter.maxBands)
for i=1,r do
c.bands[#c.bands+1]=AddBand()
end
critters[#critters+1]=c
--p(c.name.." bands: "..#c.bands)
end
function AddBand() --create one random band
local cnt=0
local b={}
while true do --try till die
cnt=cnt+1
local s1=random(segCnt)
if onlyMutable==true or #UseSegments>0 then
s1=UseSegments[random(#UseSegments)]
end
if #UseSegments>0 or CanBeUsed(s1) then
local str=random(bands.minStr,bands.maxStr)
local length=random(bands.minUp,bands.maxUp) --debugged BK 15/2/2015, length cannot be 0
local theta = math.acos(math.random())
local phi = 2 * math.pi * math.random()
local segmentXAxis=0
local segmentYAxis=0
while true do --all 3 must be different
segmentXAxis = random(segCnt)
segmentYAxis = random(segCnt)
if segmentXAxis~=s1 and segmentYAxis~=s1 and segmentXAxis~=segmentYAxis then break end
end
--{segmentOrigin, segmentXAxis, segmentYAxis, rho, theta, phi}
b={s1, segmentXAxis, segmentYAxis, length, theta, phi, str}
break
end
if cnt>100 then
p("Sorry! Cant create band! Breaking script!")
BreakScript() --there is no such function, so it crashes script
end
end
return b
end
function deletebands() --leave user bands intact
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 -- for i
end -- if nbands
end -- function
function CanBeUsed(sg1) --checking end of bands
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
local ok=true
if #DoNotUse>0 then --none of 2 can be in that area
for i=1, #DoNotUse do
local r=DoNotUse[i] -- r={from,to}
for x=r[1],r[2] do
if x==sg1 then
ok=false
break
end
end
if ok==false then break end
end
end
if ok==false then
return false --if false can`t be used
else
ok=false
if #AlwaysUse>0 then --at least one have to be there
for i=1, #AlwaysUse do
local r=AlwaysUse[i]
for x=r[1],r[2] do
if x==sg1 then
ok=true
break
end
end
if ok==true then break end
end
else
ok=true
end
end
if ok==true then --check structure
ok=false
if ssCheck(structure.GetSecondaryStructure(sg1)) then ok=true end
end
return ok
end
function ScoreHerd() --score all critters from herd. EXPERIMENTAL: weighted scores
save.Quickload(3)
p("Scoring "..#critters.." critters...")
save.Quicksave(5)
local herdScore=Score(true)
for i=1,#critters do
--band.DeleteAll()
deletebands()
local crt=critters[i] --critter
local s=Score(true) --start score
local bnds=crt.bands
for b=1,#bnds do
local bnd=bnds[b]
local atom=5
local sn=bnd[1]
if sn==segCnt then atom=6 end --bug in last segment
if structure.GetAminoAcid(sn)=='g' then atom=0 end --glycyne
band.Add(sn,bnd[2],bnd[3],bnd[4],bnd[5],bnd[6],atom)
local bc=band.GetCount()
band.SetStrength(bc,bnd[7])
end
selection.SelectAll()
CI(pullCI)
recentbest.Save()
Wiggle("wb",1)
--band.DeleteAll()
deletebands()
CI(1)
if s-Score(true) > qstabThresh and FiltersOK() then -- NEW testing if filters nok, jump this
Qstab()
else
Wiggle()
end
if Score()-bestScore>fuzeThresh and FiltersOK() then
SaveRB()
Fuze()
else
SaveRB()
end
crt.score=Score(true)-s
--p("Critter "..crt.name.." : "..round(crt.score).." pts.")
if critter.maxLoss>0 then
if Score(true)>herdScore-critter.maxLoss then
save.Quicksave(5)
herdScore=Score(true)
else
save.Quickload(5)
end
else
save.Quickload(3)
end
end
save.Quickload(3)
if band.GetCount()>0 then --clean bands from best solution (if any)
--band.DeleteAll()
deletebands()
save.Quicksave(3)
end
end
function BreedCritters(mom,dad,t) --breed 2 critters. bands are taken randomly
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 --kid have bands count between mom and dad bands
local bn=random(mb,db)
for i=1,bn,2 do
kid.bands[#kid.bands+1]=mom.bands[random(#mom.bands)]
kid.bands[#kid.bands+1]=dad.bands[random(#dad.bands)]
end
--p(kid.name.." bands: "..#kid.bands)
return kid
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 then
newHerd[#newHerd+1]=critters[i]
end
end
return newHerd
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 BreedHerd()
p("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=random(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 ShuffleHerd()
if herd.shuffle==true then
for i=1,#critters do
local r=random(#critters)
if r~=i then
critters[i],critters[r]=critters[r],critters[i]
end
end
end
end
function GAB()
if onlyMutable==true then
for i=1,segCnt do
if structure.IsMutable(i) then
UseSegments[#UseSegments+1]=i
end
end
end
critterID=0
gen=0
ss=Score()
bestScore=ss
save.Quicksave(3)
recentbest.Save()
p("Rav3n_pl Bands! Bands in space! GAB BiS")
p("Start score: "..round(ss))
if energy then
energy=false
ssNormal=Score()
p("Normal score: "..round(ssNormal).. " Conditions= "..ss-ssNormal)
energy=true
end
critters={}
FillHerd()
badGen=0
ssstr=(ssstr..round(ss)) -- jeff101
while true do --this is (almost) endless script ;]
undo.SetUndo(false) -- reset each loop if gain will give a long list of interesting undos
genScore=Score()
gen=gen+1
p()
p("Generation: "..gen..", score: "..round(Score())..", gain: "..round(Score()-ss))
ShuffleHerd()
ScoreHerd() -- weighted (EXPERIMENTAL)
save.Quickload(3)
if gen==herd.maxGen then break end --end of script
if genScore>=Score() then badGen=badGen+1 else badGen=0 end -- bug fixed thanks to robgee
if badGen>=herd.renew then
p("Creating fresh random herd...")
critters={}
FillHerd()
badGen=0
else
BreedHerd()
end
-- START jeff101's display
genEndScore=Score()
genGain=genEndScore-genScore
if gen>1 and (gen-1)==5*down((gen-1)/5) then -- New 18/1/2016 jeff101 log display
ssstr=(ssstr..'\n ') -- start new line every 5 generations
end
if genGain>=0 then
ssstr=(ssstr..' + '..round(genGain))
undo.SetUndo(true)
else
ssstr=(ssstr..' - '..(-round(genGain)))
end
p(ssstr..' = '..round(genEndScore))
--END jeff101's display
--3a) COPY from here ---------------
loop_count=gen
SetNote(note_number, starting_score, recipename, loop_count) -- WARNING: and mach "recipename" and "loop_count" with the names of the recipe
--3b) COPY to here --------------
SaveBigSteps() -- for movie at the end
end
p("Final score: "..round(Score()).." Total gain: "..round(Score()-ss))
end
--START Movie store big steps
StepScore= Score() -- New 4/11/2014
function StoreBigStep()
StepScore= Score()
save.Quicksave(SlotStep)
SlotStep=SlotStep+1
end
function SaveBigSteps() -- in order to be able to display big steps afterwards in undos
local g=Score()-StepScore
local s= Score()
if s >0 and ( (s <7000 and g>500) or (s <9000 and g>200) or (s <10000 and g>100) or (s >10000 and g>50)) then
StoreBigStep()
end
end
function MovieSteps()
undo.SetUndo(true)
for i= SlotStepStart, SlotStep do
save.Quickload(SlotStep)
end
--save.Quickload(3)
print("See remembering of big steps in undos")
end
--END Movie store big steps
function Cleanup(err) -- NEW BK 10/10/2013
start,stop,line,msg=err:find(":(%d+):%s()")
err=err:sub(msg,#err)
print('---')
if err:find('Cancelled')~=nil then
print("User stop.")
else
print("unexpected error detected.")
--p("Error line: %i.")
--p("Error : %s.")
p("Error line: ", line)
p("Error: ", err)
end
print("Restoring CI, best result, filter and structures")
CI(1)
undo.SetUndo(true)
if FILTERMANAGE then return2OriginalFilterSetting() end -- always back to user settings
StoreBigStep()
MovieSteps()
save.Quickload(3)
--3a) COPY from here ---------------
loop_count=gen
SetNote(note_number, starting_score, recipename, loop_count) -- WARNING: and mach "recipename" and "loop_count" with the names of the recipe
--3b) COPY to here --------------
deletebands() -- WARNING must be defined anywhere !!
print(err)
end
function dialogOptions()
local mut=false
for i=1,segCnt do
if structure.IsMutable(i) then mut=true break end
end
if not mut then
onlyMutable=false
mutateOnce=false
end
opt = dialog.CreateDialog("Main Options")
opt.lbl1 = dialog.AddLabel("GAB BiS main options:")
opt.gen = dialog.AddTextbox("Generations:", herd.maxGen)
opt.pull= dialog.AddSlider("Pulling CI", pullCI, 0.05, 1, 2)--desctip, default, min, max, precision
opt.shak= dialog.AddSlider("Shake CI", shakeCI, 0.01, 1, 2)
opt.maxci= dialog.AddSlider("Maximum CI", maxCI, 0.05, 1, 2)
opt.qstab=dialog.AddCheckbox("FastQstab", fastQstab)
opt.energy=dialog.AddCheckbox("Seek energy", energy) -- will be better on on exploration puzzles
opt.mutonly=dialog.AddCheckbox("Only mutable", onlyMutable)
opt.mutonce=dialog.AddCheckbox("Mutate once (selected)", mutateOnce)
opt.mutalways=dialog.AddCheckbox("Mutate always (selected)", mutateAlwas)
opt.lbl6 = dialog.AddLabel("Note: never mutate all")
opt.fuzeth=dialog.AddTextbox("Fuze threshold: ",fuzeThresh)
opt.qsth=dialog.AddTextbox("Qstab threshold: ",qstabThresh)
if PROBABLEHBOND then
opt.HBONDING=dialog.AddCheckbox("Only Hbonding AAs", HBONDING)
opt.NOHBONDING=dialog.AddCheckbox("Or only not Hbonding AAs", NOHBONDING)
end
if HASLIGAND then
opt.AlwaysUseLigand=dialog.AddCheckbox("Always use ligand: ",AlwaysUseLigand)
end
if SKETCHBOOK then
opt.minGain= dialog.AddSlider("Min gain/it.", minGain, 0, 50, 1)
end
if EXPLORATION then
opt.conditions=dialog.AddCheckbox("Check conditions", conditions)
end
if PROBABLEFILTER then
opt.FILTERMANAGE=dialog.AddCheckbox("Fast filter during wiggle only: ",FILTERMANAGE)
opt.GENERICFILTER=dialog.AddCheckbox("Fast filter unless for score: ",GENERICFILTER)
opt.lbl5 = dialog.AddLabel("Warning: no weight during fast filter")
end
opt.run = dialog.AddButton("Start", 1)
opt.more = dialog.AddButton("More", 2)
opt.cancel = dialog.AddButton("Cancel (???)", 0)
res=dialog.Show(opt)
if res>0 then
energy=opt.energy.value
pullCI=opt.pull.value
shakeCI=opt.shak.value
maxCI=opt.maxci.value
fastQstab=opt.qstab.value
fuzeThresh = tonumber(opt.fuzeth.value)
qstabThresh= tonumber(opt.qsth.value)
onlyMutable=opt.mutonly.value
mutateOnce=opt.mutonce.value
mutateAlwas=opt.mutalways.value
herd.maxgen=tonumber(opt.gen.value)
if PROBABLEHBOND then
HBONDING=opt.HBONDING.value
NOHBONDING=opt.NOHBONDING.value
end
if HASLIGAND then
AlwaysUseLigand=opt.AlwaysUseLigand.value
DoAlwaysUseLigand()
end
if SKETCHBOOK then
minGain=opt.minGain.value
end
if PROBABLEFILTER then
FILTERMANAGE=opt.FILTERMANAGE.value
GENERICFILTER=opt.GENERICFILTER.value
end
if EXPLORATION then
conditions=opt.conditions.value
end
if res==2 then
opt = dialog.CreateDialog("More Options")
opt.lbl1 = dialog.AddLabel("Herd:")
opt.brbest=dialog.AddSlider("Breed best", herd.breedBest, 1, 20, 0)
opt.kbest=dialog.AddSlider("Keep best", herd.keepBest, 1, 20, 0)
opt.bas=dialog.AddSlider("Breed bastards", herd.breedBastards, 1, 20, 0)
opt.rnd=dialog.AddSlider("New random", herd.newRandom, 1, 30, 0)
opt.renew=dialog.AddSlider("Renew hers", herd.renew, 1, 10, 0)
opt.shuff=dialog.AddCheckbox("Shuffle critters", herd.shuffle)
opt.lbl2 = dialog.AddLabel("Critter:")
opt.minb=dialog.AddSlider("Minimum bands:", critter.minBands, 1,4,0)
opt.maxb=dialog.AddSlider("Maximum bands:", critter.maxBands,4,50,0)
opt.keeps=dialog.AddSlider("Keep score:", critter.keepScore, -10, 10, 0)
opt.breeds=dialog.AddSlider("Breed score:", critter.breedScore, -40, 10,0)
opt.maxloss=dialog.AddSlider("Max loss:", critter.maxLoss, 0,50,0)
opt.lbl4 = dialog.AddLabel("(0 to disable max loss)")
opt.lbl3 = dialog.AddLabel("Bands:")
opt.minst=dialog.AddSlider("Min str", bands.minStr, 0.1, 2, 1)
opt.maxst=dialog.AddSlider("Max str", bands.maxStr, 0.2, 2, 1)
opt.maxup=dialog.AddSlider("Max lenght", bands.maxUp, 1, 20, 0)
opt.run = dialog.AddButton("Start", 1)
dialog.Show(opt)
herd.breedBest = opt.brbest.value
herd.keepBest = opt.kbest.value
herd.breedBastards = opt.bas.value
herd.newRandom = opt.rnd.value
herd.shuffle = opt.shuff.value
herd.renew=opt.renew.value
critter.minBands=tonumber(opt.minb.value)
critter.maxBands=tonumber(opt.maxb.value)
critter.keepScore = tonumber(opt.keeps.value)
critter.breedScore=tonumber(opt.breeds.value)
critter.maxLoss=tonumber(opt.maxloss.value)
bands.minStr=opt.minst.value
bands.maxStr=opt.maxst.value
bands.maxUp = opt.maxup.value
end
else
p("Exiting...")
BreakScript()
end
end
-- main call
function MAIN()
dialogOptions()
getShortDistSym()
GenericFilterMngt()
GAB()
end
xpcall(MAIN,Cleanup)
--end of script