Icon representing a recipe

Recipe: Mutate No Wiggle 1.3.2 -- Brow42

created by bertro

Profile


Name
Mutate No Wiggle 1.3.2 -- Brow42
ID
102991
Shared with
Public
Parent
Mutate No Wiggle 1.3.1 -- Brow42
Children
Created on
January 13, 2019 at 14:06 PM UTC
Updated on
January 13, 2019 at 14:06 PM UTC
Description

Brute Force Mutator, but doesn't wiggle, stabilize, or fuze. Keeps best AA for each spot. Good for slow design puzzles. Has options for restricting mutations. 1.2.1 bans alanines. 1.3 re-allows p,g,a,t for loops.

Best for


Code


--[[ * Mutate No Wiggle -- Brow42 * Original Author: Brow42 * Version 1.0 Sept 6 2014 * Brute-force mutator without the wiggle...for * puzzles where wiggle is too slow. * Also has options to restrict which amino acids * are mutated, and to what. For example, prolines * and glycines won't be added, and cysteines won't * be touched. * Automatically does all mutables from worst to best * scoring, but range can be set manually, or via * selection/freeze before you run the script. * This script uses strings instead of tables for AAs. * * Version 1.02 -- Republished under new id * * Version 1.1 Oct 31 2014 Brow42 * * Added pause to allow GUI to function. Added timing info * Added option to remove unwanted residues (from Googol30) * Added trap for User Cancel * * Version 1.1.1 May 14 2015 Brow42 * * Allowed mutation of locked segments as requested by LociOling * * Version 1.2 May 18 2015 Brow 42 * * Added options to quickly select only hydrophillics for bonding * Added more options to select/exclude polar and aliphatic AAs * Rearranged menus to accomodate more options * More error checking, such as empty mutate lists * * Version 1.21 July 30 2015 * * Added option to eliminate alanines * * Version 1.3 Nov 23 2015 * * Added option to allow aa's in loops * Recoded so each SS has it's own allow/deny. * * Version 1.3.1 Nov 30 2015 * * Bug fix to aas array. * * Version 1.3.2 Jan 13 2019 * Wbertro * Added threonine --]] NSeg = structure.GetCount() title = "Brute Force Mutate No-Wiggle 1.3.1" options = { cok=true, pok=true, gok=true, aok=true, tok=true, keepc=false, keepp=false, keepg = false, keept = false, keephydro=false, charged=true, rings=true, custom="", range="", removeaa=false, lockedok=true, philliconly=false, phobiconly=false, phillic=true, phobic=true, anyloop=false} -- Gah. All the options have to be in the dialog, so these options are not in the options table guitext_template = "%0.2f seconds every %d mutations" pause = 5 delay = 0.1 options["gui"] = guitext_template:format(delay,pause) -- Menu Button Enums OKAY=1 CANCEL=0 SELECT=2 ABOUT=3 AASELECT=4 CLEAR=5 ALL=6 REPEAT=-1 function MainMenu() local d = dialog.CreateDialog(title) if options.custom == '' then d["label1"] = dialog.AddLabel("AA Selection shortcuts (or use button below).") d["philliconly"] = dialog.AddCheckbox("Hydrophilic (blue) only (no phobics).",options.philliconly) d["phobiconly"] = dialog.AddCheckbox("Hydrophobic (orange) only (no phillics).",options.phobiconly) else d["label1"] = dialog.AddLabel("AAs have been selected on the AA Allowed page.") end d["lable2"] = dialog.AddLabel("Don't change these AAs:") d["keepc"] = dialog.AddCheckbox("cysteine",options.keepc) d["keepp"] = dialog.AddCheckbox("proline",options.keepp) d["keepg"] = dialog.AddCheckbox("glycine",options.keepg) d["keept"] = dialog.AddCheckbox("threonine",options.keept) d["range"] = dialog.AddTextbox("Where:",options.range) d["removeaa"] = dialog.AddCheckbox("Remove AAs not marked allowed or don't change",options.removeaa) d["keephydro"] = dialog.AddCheckbox("Preserve Hydrophobicity",options.keephydro) d["gui"] = dialog.AddTextbox("GUI Saver: Wait",options.gui) d["ok"] = dialog.AddButton("OK",OKAY) d["cancel"] = dialog.AddButton("Cancel",CANCEL) d["where"] = dialog.AddButton("Select",SELECT) d["aaselect"] = dialog.AddButton("Allow AA",AASELECT) local rc = dialog.Show(d) for u,v in pairs(options) do pcall(function() options[u] = d[u].value end) -- using scoping so I don't have to pass any variable names end if options.philliconly and options.phobiconly then ErrorMessage("Don't check both phillic-only and phobic-only!","AA Selection") rc = REPEAT end segs,err = RangeToList(ParseRange(options.range)) if err ~= "" then ErrorMessage(err,"Range Input") rc = REPEAT end err = ParseWait(options.gui) if err then ErrorMessage(err,"GUI Saver Delay Parameters") rc = REPEAT end -- change how allowed AA panel looks if options.philliconly then options.cok = false options.pok = false options.gok = false options.aok = false options.tok = false options.phobic = false options.rings = false end if options.phobiconly then options.charged = false options.phillic = false end return rc, MainMenu end function AASelect() local d = dialog.CreateDialog(title) if options.custom == '' then d["label1"] = dialog.AddLabel("Include these as allowed AA (previous page overrides):") d["cok"] = dialog.AddCheckbox("cysteine",options.cok) d["pok"] = dialog.AddCheckbox("proline",options.pok) d["gok"] = dialog.AddCheckbox("glycine",options.gok) d["aok"] = dialog.AddCheckbox("alanine",options.aok) d["tok"] = dialog.AddCheckbox("threonine",options.tok) d["anyloop"] = dialog.AddCheckbox("But allow ala, pro, gly in loops anyways",options.anyloop) d["charged"] = dialog.AddCheckbox("charged hydrophillics",options.charged) d["phillic"] = dialog.AddCheckbox("other (polar) hydrophillics",options.phillic) d["rings"] = dialog.AddCheckbox("rings (aromatic) hydrophobics: phen, tryp, tyr",options.rings) d["phobic"] = dialog.AddCheckbox("other (aliphatic, branched, not c,p,g) hydrophobics", options.phobic) d["label2"] = dialog.AddLabel("Or, specify AA (overrides all checkboxes):") else dialog.AddLabels(d,{"AAs have been selected below.","Clear to see options again."}) end d["custom"] = dialog.AddTextbox("AAs",options.custom) d["ok"] = dialog.AddButton("Accept",REPEAT) if options.custom ~= '' then d["clear"] = dialog.AddButton("Clear",CLEAR) else d["all"] = dialog.AddButton("Allow All",ALL) end d["help"] = dialog.AddButton("About",ABOUT) local rc = dialog.Show(d) if rc == CANCEL then return rc end if rc == CLEAR then options.custom = '' return REPEAT, AASelect end if rc == ALL then options.cok = true options.pok = true options.gok = true options.aok = true options.tok = true options.charged = true options.rings = true options.phillic = true options.phobic = true options.phobiconly = false options.philliconly = false return REPEAT, MainMenu end for u,v in pairs(options) do pcall(function() options[u] = d[u].value end) -- using scoping so I don't have to pass any variable names end if options.custom ~= "" and options.custom:find("[^"..all_aa..all_aa:upper().."]") then ErrorMessage("You gave something other than one of the 20 AAs.") return REPEAT, AASelect end return rc, MainMenu end -- ================================== Begin New Dialog Library Functions -- Add a wall of text from a table function dialog.AddLabels(d,msg,nlabels) -- pass in # of existing autolabels local nlabels = nlabels or #(d._Order or {}) -- default, valid if never delete dialog elements if type(msg) == 'string' then msg = { msg } end for i = 1,#msg do d['autolabel'..tostring(i+nlabels)] = dialog.AddLabel(msg[i]) end end -- Create but don't display a wall of text and 1 or 2 buttons function dialog.CreateMessageBox(msg,title,buttontext1,buttontext0,buttontext2) title = title or '' local d = dialog.CreateDialog(title) dialog.AddLabels(d,msg) buttontext1 = buttontext1 or 'Ok' d.button = dialog.AddButton(buttontext1,1) if buttontext2 ~= nil then d.button2 = dialog.AddButton(buttontext2,2) end if buttontext0 ~= nil then d.button0 = dialog.AddButton(buttontext0,0) end return d end -- Display a dialog box function dialog.ShowMessageBox(msg,title,buttontext1,buttontext0,buttontext2) return dialog.Show(dialog.CreateMessageBox(msg,title,buttontext1,buttontext0,buttontext2)) end -- Display a box AND print to the output function ErrorMessage(msg,title) if type(msg) == 'string' then msg = { msg } end for i = 1,#msg do print(msg[i]) end return dialog.ShowMessageBox(msg,title) end -- ======================================= End Dialog Functions function About() local rc = dialog.ShowMessageBox( { "This recipe mutates all mutables to all amino acids", "keeping the best score for each segment. It does this", "from worst score to best score. NO WIGGLE IS DONE.", "", "Skipping wiggle allows this to work on puzzles with", "very slow wiggle, but probably won't find the same", "combination as one of the regular mutation scripts.", "", "You can control which segments are mutated, and to", "what types of amino acids. In particular, you can", "prevent prolines and glycines from being added. You", "can also prevent cysteines from being mutated to save", "bridges. You can restrict mutations to the same", "hydrophobicity." },title,"Back") if rc == CANCEL then return CANCEL end -- cancel script return REPEAT, AASelect end -- Turn selection or frozen into ranges and range string RETURN=REPEAT function Where() local d = dialog.CreateDialog(title) dialog.AddLabels(d, {"Have you selected segments to muate with","the selection or freeze tool?"}) d.select = dialog.AddButton("Selection",1) d.freeze = dialog.AddButton("Freeze",2) d.nope = dialog.AddButton("Nope",RETURN) -- 0 is window close (cancel script) local rc = dialog.Show(d) if rc == CANCEL or rc == RETURN then return rc, MainMenu end local tmp = {selection.IsSelected, freeze.IsFrozen} UserSelect = tmp[rc] -- pretty name because it shows up in the GUI local appending = false local s local range local list = {} for i = 1, NSeg do s = UserSelect(i) if s then if appending then range[2] = i else range = {i,i} list[#list+1] = range appending = true end else if appending then appending = false end end end for i = 1,#list do if list[i][1] == list[i][2] then list[i] = tostring(list[i][1]) else list[i] = tostring(list[i][1]).."-"..tostring(list[i][2]) end end options.range = table.concat(list," ") return REPEAT, MainMenu end -- sort by score, also filter by mutability function SortSegments(segs) local scores = {} local keys = {} for j = 1,#segs do i = segs[j] ss = structure.GetSecondaryStructure(i):upper() if structure.IsMutable(i) and (options.lockedok or not structure.IsLocked(i) and ( ss=='E' or ss=='H' or ss=='L') ) then s = current.GetSegmentEnergyScore(i) - current.GetSegmentEnergySubscore(i,'reference') scores[s] = i -- scores map to segments keys[#keys+1] = s -- keys are scores end end table.sort(keys) for i = 1,#keys do keys[i] = scores[keys[i]] -- replace score with segment end return keys end -- Turn a range string into a list of ranges (allows negatives, skips non-numerica) function ParseRange(str) local ranges = {} local index while true do local a,b,x = str:find("(%-?%d+)") -- next number local c,d,y,z = str:find("(%d+)%s*%-%s*(%d+)") -- next range if a and ( c == nil or a < c ) then x = tonumber(x) ranges[#ranges+1] = { x, x } index = b+1 elseif c then y = tonumber(y) z = tonumber(z) ranges[#ranges+1] = { y, z } index = d+1 else break end str = str:sub(index+1,str:len()) end return ranges end -- Parse the GUI Saver text options function ParseWait(str) local numpat = "(%-?%d*%.?%d*)" local notnumpat = "[^%d-.]*" local _,_,a,b = str:find(notnumpat..numpat..notnumpat..numpat) if options.gui == '' then pause,delay = 0,0 return end options["gui"] = guitext_template:format(delay,pause) local errstate = a == '' or a == nil or b == '' or b == nil if errstate ~= false then return {"To give the GUI a chance to draw, you can wait a", "certain amount of time after a fixed number of", "mutations. Just specify either 0 seconds or 0 mutations", "to go as fast as possible." } end a,b = tonumber(a),tonumber(b) -- doesn't seem like this can crash on any input matching numpat errstate = a == '' or a == nil or b == '' or b == nil -- Something a little goofy having to check this twice if errstate ~= false then return {"To give the GUI a chance to draw, you can wait a", "certain amount of time after a fixed number of", "mutations. Just specify either 0 seconds or 0 mutations", "to go as fast as possible." } end if a < 0 or b < 0 then return {"Looks like you entered a negative number.","Zero or greater only please."} end if a < 0.01 and b > 0 then return {"Did I say zero? Actually, 0.01 seconds is the","shortest delay."} end -- all good pause,delay = b,a options["gui"] = guitext_template:format(delay,pause) return end -- Turn list of ranges into a list of segments function RangeToList(ranges) if #ranges == 0 then ranges = {{1,NSeg}} end local list = {} local err = "" for i = 1,#ranges do local a,b = unpack(ranges[i]) if a < 1 or a > NSeg then return list,tostring(a).." is out of range." end if b < 1 or b > NSeg then return list,tostring(b).." is out of range." end for j = a,b do list[j] = true end end local list2 = {} for u,v in pairs(list) do list2[#list2+1] = u end table.sort(list2) return list2,err end -- =================== Begin Main ========================== print(title) print (puzzle.GetName(),current.GetScore()) -- different classes of AAs charged = "rkdeh" philic = "qnst" -- excluding charged phobic = "cmailvpg" -- excluding rings rings = "fwy" all_aa = charged..philic..phobic..rings Menu = MainMenu -- user will see variable name aas = '' while aas == '' do repeat rc,Menu = Menu() if rc == CANCEL then print ("User exit.") return end if rc == SELECT then Menu = Where end if rc == ABOUT then Menu = About end if rc == AASELECT then Menu = AASelect end if rc == CANCEL then return end -- cancels from child dialogs until rc == OKAY if options.custom ~= "" then aas = options.custom else if options.phillic and not options.phobiconly then aas = aas..philic end if options.phobic and not options.philliconly then aas = aas..phobic end if options.charged and not options.phobiconly then aas = aas..charged end if options.rings and not options.philliconly then aas = aas..rings end if not options.cok then aas = aas:gsub('c','') end if not options.pok then aas = aas:gsub('p','') end if not options.gok then aas = aas:gsub('g','') end if not options.aok then aas = aas:gsub('a','') end if not options.tok then aas = aas:gsub('t','') end end if aas == '' then ErrorMessage("You've excluded all the AAs from your mutation list!","AA Selection") end end aas = { H= aas, E=aas, L=aas } if options.custom == '' and options.anyloop then print ("Re-enabling p,g,a for loops if they were disabled in general...") if not options.pok then aas['L'] = aas['L']..'p' end if not options.gok then aas['L'] = aas['L']..'g' end if not options.aok then aas['L'] = aas['L']..'a' end if not options.tok then aas['L'] = aas['L']..'t' end end if delay == 0 or pause == 0 then print ("No delay between mutations.") else print ("Waiting "..options.gui) print ("Maximum possible mutation rate:",pause/delay,"Mutations/second") end print("Allowed AAs:") print (" Helix:",aas['H']:upper()) print ("Sheet:",aas['E']:upper()) print (" Loop:",aas['L']:upper()) -- okay add in the subtypes, for checking type later philic = philic..charged phobic = phobic..rings -- build preserve list from the checkboxes preserve = "" if options.keepc then preserve = preserve.."c" end if options.keepp then preserve = preserve.."p" end if options.keepg then preserve = preserve.."g" end if options.keept then preserve = preserve.."t" end start = current.GetScore() order = SortSegments(segs) start_time = os.time() n_mutations = 0 speedcheck_n = 100 -- every 100 mutations (5 segments?) speedcheck_s = 120 -- every 2 minutes lastspeedcheck_s = start_time lastspeedcheck_n = 0 function PrintSpeedCheck(force) local t = os.time() -- probably won't wrap around local dt = t - start_time if force or t-lastspeedcheck_s > speedcheck_s or n_mutations-lastspeedcheck_n > speedcheck_n then lastspeedcheck_s = t lastspeedcheck_n = n_mutations print ("Average mutation rate:",n_mutations/dt,"mutations/second") end end function Main() for i = 1,#order do recentbest.Save() s = current.GetScore() iSeg = order[i] -- Not Local origaa = structure.GetAminoAcid(iSeg) -- Not Local if preserve:find(origaa) == nil then -- skip AAs in keep list first_mutation = true -- first mutation this segment ss = structure.GetSecondaryStructure(iSeg):upper() for j =1,aas[ss]:len() do aa = aas[ss]:sub(j,j) hydrook = not options.keephydro or (structure.IsHydrophobic(iSeg) and phobic:find(aa) ~= nil) or (not structure.IsHydrophobic(iSeg) and philic:find(aa) ~= nil) if hydrook then -- if correct type, else skip structure.SetAminoAcid(iSeg,aa) n_mutations = n_mutations + 1 if first_mutation and options.removeaa == true and aas[ss]:find(origaa) == nil then -- if removing non-allowed AAs recentbest.Save() -- forget the original score of the unwanted aa, use this one end first_mutation = false end -- GUI pause here if delay > 0 and pause > 0 and n_mutations > 0 and n_mutations % pause == 0 then t = os.clock() repeat dt = os.clock() - t -- watch out, clock() can wrap to 0 until dt > delay or dt < 0 end PrintSpeedCheck(false) end recentbest.Restore() print( i, "of", #order, "Segment #", iSeg, origaa, "->", structure.GetAminoAcid(iSeg), "change", current.GetScore()-s ) else print( i, "of", #order, "Segment #", iSeg, origaa, "skipped" ) end end end function OnError(errmsg) if string.find(errmsg,"Cancelled") then print("User cancel. Loading recent best.") recentbest.Restore() else -- it's a real error, not a cancel print(errmsg) end return errmsg end rc, err = xpcall(Main,OnError) if rc == true then print("Final score",current.GetScore(), current.GetScore()-start) PrintSpeedCheck(true) end

Comments