Icon representing a recipe

Recipe: Mutate No Wiggle 1.5

created by LociOiling

Profile


Name
Mutate No Wiggle 1.5
ID
101107
Shared with
Public
Parent
Mutate No Wiggle 1.4
Children
Created on
June 11, 2015 at 21:53 PM UTC
Updated on
June 11, 2015 at 21:53 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. Version 1.2 lets you exclude alanine. Version 1.3 works on locked segments and lets you repeat a specified number of times. Version 1.4 shuffles the order of segments after the first round. The first round is in segment score order. Version 1.5 sets an adjustable minimum gain threshold to avoid wasting time on miniscule gains.

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 Version 1.1 Dec 18 2014 LociOiling 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 Apr 19 2015 LociOiling * Allowed mutation of locked segments. Version 1.3 2015/05/16 LociOiling * loop Version 1.4 2015/05/16 LociOiling * shuffle order of segments each loop Version 1.5 2015/06/10 LociOiling * add minimum gain setting ]]-- NSeg = structure.GetCount() title = "Mutate No Wiggle 1.5" options = { cok=true, pok=true, gok=true, aok=true, keepc=false, keepp=false, keepg = false, keephydro=false, charged=true, rings=true, custom="", range="", removeaa=false, locked=true, repeats=10, maxbad=2, mingain=2, } -- 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) function Menu() local d = dialog.CreateDialog(title) d["label1"] = dialog.AddLabel("Include these as allowed AA") 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["charged"] = dialog.AddCheckbox("charged AA",options.charged) d["rings"] = dialog.AddCheckbox("rings: phen, tryp, tyr",options.rings) d["label2"] = dialog.AddLabel("Or, specify AA (overrides above)") d["custom"] = dialog.AddTextbox("AAs",options.custom) d["lable2"] = dialog.AddLabel("Don't change these AA") d["keepc"] = dialog.AddCheckbox("cysteine",options.keepc) d["keepp"] = dialog.AddCheckbox("proline",options.keepp) d["keepg"] = dialog.AddCheckbox("glycine",options.keepg) d["range"] = dialog.AddTextbox("Where:",options.range) -- d["label3"] = dialog.AddLabel("") d["keephydro"] = dialog.AddCheckbox("Preserve Hydrophobicity",options.keephydro) d["removeaa"] = dialog.AddCheckbox("Remove AAs not in either list above (if possible)",options.removeaa) d["locked"] = dialog.AddCheckbox("Include locked",options.locked) d["repeats"] = dialog.AddSlider ( "Repeats", options.repeats, 1, 10, 0 ) d["maxbad"] = dialog.AddSlider ( "Max reps no gain", options.maxbad, 1, 10, 0) d["mingain"] = dialog.AddSlider ( "Min gain per rep", options.mingain, 1, 25, 0) d["gui"] = dialog.AddTextbox("GUI Saver: Wait",options.gui) d["ok"] = dialog.AddButton("OK",1) d["cancel"] = dialog.AddButton("Cancel",0) d["where"] = dialog.AddButton("Select",2) d["help"] = dialog.AddButton("About",3) 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 return rc 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 == 0 then return 0 end -- cancel script return 3 end -- Turn selection or frozen into ranges and range string function Where() local d = dialog.CreateDialog(title) dialog.AddLabels(d, {"Have you selected segments to mutate with","the selection or freeze tool?"}) d.select = dialog.AddButton("Selection",1) d.freeze = dialog.AddButton("Freeze",2) d.nope = dialog.AddButton("Nope",3) -- 0 is window close (cancel script) local rc = dialog.Show(d) if rc == 0 or rc == 3 then return rc 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 2 end -- sort by score, also filter by mutability function SortSegments(segs) local scores = {} local keys = {} for j = 1,#segs do i = segs[j] if structure.IsMutable(i) and ( options.locked or not structure.IsLocked(i) ) 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 -- Thanks too Rav4pl function ShuffleTable(tab) --randomize order of elements local cnt=#tab for i=1,cnt do local r=math.random(cnt) tab[i],tab[r]=tab[r],tab[i] end return tab 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" deny = "" preserve = "" all_aa = charged..philic..phobic..rings repeat rc = Menu() if rc == 0 then return end segs,err = RangeToList(ParseRange(options.range)) if err ~= "" then ErrorMessage(err,"Range Input") rc = -1 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.") rc = -1 end err = ParseWait(options.gui) if err then ErrorMessage(err,"GUI Saver Delay Parameters") rc = -1 end if rc == 2 then rc = Where() end if rc == 3 then rc = About() end if rc == 0 then return end -- cancels from child dialogs until rc == 1 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 if options.custom ~= "" then aas = options.custom else aas = philic..phobic if options.charged then aas = aas..charged end if options.rings then aas = aas..rings end end -- okay add in the subtypes, for checking type later philic = philic..charged phobic = phobic..rings -- build preserve and deny lists from the checkboxes 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 not options.cok then deny = deny.."c" end if not options.pok then deny = deny.."p" end if not options.gok then deny = deny.."g" end if not options.aok then deny = deny.."a" 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 keep first_mutation = true -- first mutation this segment for j =1,aas:len() do aa = aas: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 deny:find(aa) == nil and hydrook then -- if not in deny and correct type, else skip structure.SetAminoAcid(iSeg,aa) n_mutations = n_mutations + 1 if first_mutation and options.removeaa == true and (deny:find(origaa) or aas:find(origaa) == nil) then 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 Mainline () local badreps = 0 for ii = 1, options.repeats do local loopstart = current.GetScore() print ( "begin run " .. ii .. ", score = " .. loopstart ) Main() local loopend = current.GetScore() print ( "end run " .. ii .. ", gain = " .. loopend - loopstart ) if loopend - loopstart < options.mingain then badreps = badreps + 1 else badreps = 0 end if badreps >= options.maxbad then break end order = ShuffleTable ( order ) 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(Mainline,OnError) if rc == true then print("Final score",current.GetScore(), current.GetScore()-start) PrintSpeedCheck(true) end

Comments