Icon representing a recipe

Recipe: HNetwork Probe 2.3

created by LociOiling

Profile


Name
HNetwork Probe 2.3
ID
104052
Shared with
Public
Parent
HNetwork Probe 2.1
Children
Created on
October 14, 2020 at 19:37 PM UTC
Updated on
October 14, 2020 at 19:37 PM UTC
Description

New options, including wiggle factor, skipping initial mutates, and shuffling the list of segments to mutate. V2.3 adds named saves.

Best for


Code


--[[ HNetwork Probe 2.1 - spvincent - 8 October 2020 2.2 - LociOiling - 12 October 2020 + optionally skip initial mutation of all segments + optionally shuffle the list of mutable segments + gracefully handle cases where there's nothing to do + fluffier dialog + more complete cleanup, catch script errors + some minor polishing, Luafication, and Locifaction + print progress messages, more if VERBOSE + filch some EDRW code from TvdL + pilfer wiggle factor from Timo, too + outright theft of better randomseed from KarenCH + "borrow" ShuffleTable from Rav3n_pl + validate those AA codes 2.3 - LociOiling - 13 October 2020 + make named saves when hydrogen bond network score improves + report initial score and filter scores + track overall score and filter changes + reset filters at end, report final score + don't save solutions with the same overall score + reserve slot 1 for starting pose + restore starting pose at end if no gain ]]-- Recipe = "HNetworkProbe" Version = "2.3" ReVersion = Recipe .. " " .. Version -- -- globals section -- polar_aas = "stnqdehyw" n_polar_aas = 0 n_residues = 0 first_qs_slot = 13 last_qs_slot = 35 next_qs_slot = 0 start_slot = 1 FIRST_FREE_SLOT = start_slot + 1 MAX_SLOT = 99 select_unfrozen = true sidechain_minimum = -70 n_it = 0 mutate_list_ui = {} -- will be a list of 1's & 0's for each residue. 1 means can mutate. 0 means don't mutate. mutable_list = {} filter_list = {} filter_stat = {} init_score = 0 best_score = 0 init_filter_score = 0 best_filter_score = 0 WF = 1 -- wiggle like Timo VERBOSE = false -- what's happening? skip_initial_mutate = false shuffle_mutables = true slot_stat = {} -- track the score saved in each slot -- -- AminoAcids -- -- short version of amino acids table for validation and display -- -- AminoAcids = { a = { polar = false, short = "Ala", long = "Alanine", }, c = { polar = false, short = "Cys", long = "Cysteine", }, d = { polar = true, short = "Asp", long = "Aspartate", }, e = { polar = true, short = "Glu", long = "Glutamate", }, f = { polar = false, short = "Phe", long = "Phenylalanine", }, g = { polar = false, short = "Gly", long = "Glycine", }, h = { polar = true, short = "His", long = "Histidine", }, i = { polar = false, short = "Ile", long = "Isoleucine", }, k = { polar = false, short = "Lys", long = "Lysine", }, l = { polar = true, short = "Leu", long = "Leucine", }, m = { polar = false, short = "Met", long = "Methionine", }, n = { polar = true, short = "Asn", long = "Asparagine", }, p = { polar = false, short = "Pro", long = "Proline", }, q = { polar = true, short = "Gln", long = "Glutamine", }, r = { polar = true, short = "Arg", long = "Arginine", }, s = { polar = true, short = "Ser", long = "Serine", }, t = { polar = true, short = "Thr", long = "Threonine", }, v = { polar = false, short = "Val", long = "Valine", }, w = { polar = true, short = "Trp", long = "Tryptophan", }, y = { polar = true, short = "Tyr", long = "Tyrosine", }, } -- -- end of globals section -- function r3 ( x ) -- Round to 3 decimal places t = 10 ^ 3 return math.floor ( x*t + 0.5 ) / t end -- -- get score, including metrics -- function GetScore () local scor = current.GetEnergyScore () local mbon = 0 -- -- check for the "metric" functions, -- report the total metric bonus if -- this puzzle has metrics defined -- if metric ~= nil then if metric.GetNames () ~= nil then mbon = metric.GetBonusTotal () end end return scor + mbon, mbon end function GetFilterScore () filter_bonus = filter.GetBonus ( "Hydrogen Bond Network" ) return filter_bonus end function GetListOfMutablesUnfrozen () for ii = 1, n_residues do localseg_backbone_frozen, seg_sidechain_frozen = freeze.IsFrozen ( ii ) if seg_sidechain_frozen == false -- and seg_backbone_frozen == false and structure.IsMutable ( ii ) == true then table.insert ( mutable_list , ii ) end end end function GetListOfMutablesSelected () for ii = 1, n_residues do local seg_backbone_frozen, seg_sidechain_frozen = freeze.IsFrozen ( ii ) if seg_sidechain_frozen == false and mutate_list_ui [ ii ] == 1 and structure.IsMutable ( ii ) == true then table.insert ( mutable_list , ii ) end end end -- -- getlist - pinched from Loop Rebuild 9.0 -- http://www.lua.org/manual/5.2/manual.html#6.4 -- helped make this function 11/16/17 -- function getlist ( liststr ) local newlist = {} local ilo, ihi, idir, substr -- -- the variable ii here exists for the scope -- of the for loop, no need to define another -- one, which would have different scope -- for ii = 1, n_residues do newlist [ ii ] = 0 end -- for ii -- -- the Lua regular expression below, -- used with string.gmatch, -- reads a series of substrs from liststr, -- where each substr contains an integer -- followed by one or more '-' signs, -- followed by an integer -- for substr in liststr:gmatch ( "(%d+%-+%d+)" ) do -- substr includes one or more '-' characters in a row -- -- get ilo & ihi from substr using string.gsub -- the same regex is used twice -- the regex contains two expressions in parens -- the first gsub extracts the first expression, -- the "from", and the second gsub gets the "to" -- ilo = substr:gsub ( "(%d+)%-+(%d+)", "%1" ) + 0 ihi = substr:gsub ( "(%d+)%-+(%d+)", "%2" ) + 0 idir = 1 -- the increment to use from ilo to ihi if ilo > ihi then idir = -1 end -- if ilo -- -- add an entry for each segment in the range -- any invalid entries are silently rejected here -- for ii = ilo, ihi, idir do -- for i=ilo to ihi step idir if ii >= 1 and ii <= n_residues then newlist [ ii ] = 1 end -- if ii end -- for ii end -- for substr -- -- the Lua regex below -- reads a series of substrs from liststr, -- where each substr contains an integer -- by itself -- for substr in liststr:gmatch ( "(%d+)" ) do local ij = substr + 0 -- converts substr into the number ij if ij >= 1 and ij <= n_residues then newlist [ ij ] = 1 end -- if i end -- for substr return newlist end -- -- SegmentListToSet, SegmentSetToString -- Originals by Timo van der Laan: -- 02-05-2012 TvdL Free to use for non commercial purposes -- SegmentListToSet = function ( list ) -- retirer doublons local result = {} local ff = 0 local ll = -1 table.sort ( list ) for ii = 1, #list do if list [ ii ] ~= ll + 1 and list [ ii ] ~= ll then -- note: duplicates are removed if ll > 0 then result [ #result + 1 ] = { ff, ll } end ff = list [ ii ] end ll = list [ ii ] end if ll > 0 then result [ #result + 1 ] = { ff, ll } end return result end SegmentSetToString = function ( set ) -- pour pouvoir imprimer local line = "" for ii = 1, #set do if ii ~= 1 then line = line .. ", " end line = line .. set [ ii ] [ 1 ] .. "-" .. set [ ii ] [ 2 ] end return line end -- -- seedRandom -- original by KarenCH -- -- looks for a seed > 10,000,000 and < 2 ^ 32 -- -- v2 - LociOiling - 20191103 -- * added 2 ^ 32 overflow check -- 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 seed = seed - seed % 1 print ( "Random number seed = " .. seed ) math.randomseed( seed ) -- throw away a couple of randoms math.random () math.random () end -- -- Thanks to Rav3n_pl -- 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 function Init () save.Quickload ( start_slot ) if not skip_initial_mutate then if VERBOSE then print ( "initial mutations" ) end for ii = 1, #mutable_list do local kk = math.random ( n_polar_aas ) local replacement_aa = polar_aas:sub ( kk, kk ) if VERBOSE then print ( "changing segment " .. mutable_list [ ii ] .. " to \"" .. replacement_aa .. "\"" ) end if structure.CanMutate ( mutable_list [ ii ] , replacement_aa ) then structure.SetAminoAcid ( mutable_list [ ii ] , replacement_aa ) end end end selection.SelectAll () structure.WiggleSelected ( 2 * WF, false, true ) -- sidechains end function PrintParameters () print ( "--" ) print ( "wiggle factor = " .. WF ) print ( "skip initial mutate = " .. tostring ( skip_initial_mutate ) ) print ( "shuffle mutables = " .. tostring ( shuffle_mutables ) ) print ( #mutable_list .. " mutable segments =\"" .. SegmentSetToString ( SegmentListToSet ( mutable_list ) ) .. "\"" ) print ( polar_aas:len() .. " amino acids to use = \"" .. polar_aas .. "\"" ) print ( "Sidechain minimum = " .. r3 ( sidechain_minimum ) ) print ( "Using slots " .. first_qs_slot .. " to " .. last_qs_slot ) print ( "verbose output = " .. tostring ( VERBOSE ) ) end function GetParameters () local uerror = "" local rc repeat changed = false local dlog = dialog.CreateDialog ( ReVersion ) dlog.WF = dialog.AddSlider ( "Wiggle factor" , WF, 1, 5, 0 ) dlog.skip_initial_mutate = dialog.AddCheckbox ( "Skip initial mutates" , skip_initial_mutate ) dlog.shuffle_mutables = dialog.AddCheckbox ( "Shuffle mutable list" , shuffle_mutables ) dlog.M0000 = dialog.AddLabel ( "" ) dlog.M0001 = dialog.AddLabel ( "Select unfrozen works on unfrozen sidechains:" ) dlog.use_unfrozen = dialog.AddCheckbox ( "Select unfrozen" , select_unfrozen ) dlog.M0002 = dialog.AddLabel ( "If select unfrozen is not checked," ) dlog.M0003 = dialog.AddLabel ( "segment ranges are used." ) dlog.mutate_list = dialog.AddTextbox ( "Use ranges" , "" ) dlog.mlabela = dialog.AddLabel('Specify segment ranges like 1-3,6,19-30,45-62') dlog.M0005 = dialog.AddLabel ( "Specify the amino acids to try" ) dlog.aa_list = dialog.AddTextbox ( "Amino acids" , polar_aas ) dlog.M0009 = dialog.AddLabel ( "" ) dlog.M0010 = dialog.AddLabel ( "Minimum sidechain score after mutation," ) dlog.M0011 = dialog.AddLabel ( "lower scoring mutations are skipped." ) dlog.sidechain_minimum = dialog.AddSlider ( "Sidechain minimum" , sidechain_minimum , -200 , -20 , 0 ) dlog.M0019 = dialog.AddLabel ( "" ) dlog.M0020 = dialog.AddLabel ( "Quick save slot range" ) dlog.start_qs_slot = dialog.AddSlider ( "Start quick save", first_qs_slot, FIRST_FREE_SLOT, MAX_SLOT, 0 ) dlog.end_qs_slot = dialog.AddSlider ( "End quick save", last_qs_slot, FIRST_FREE_SLOT, MAX_SLOT, 0 ) dlog.M0029 = dialog.AddLabel ( "" ) dlog.VERBOSE = dialog.AddCheckbox ( "Verbose output" , VERBOSE ) if uerror:len () > 0 then dlog.lerr1 = dialog.AddLabel ( "" ) dlog.lerr2 = dialog.AddLabel ( "ERROR: " .. uerror ) end dlog.ok = dialog.AddButton ( "OK" , 1 ) dlog.cancel = dialog.AddButton ( "Cancel" , -1 ) rc = dialog.Show ( dlog ) uerror = "" if rc > 0 then WF = dlog.WF.value skip_initial_mutate = dlog.skip_initial_mutate.value shuffle_mutables = dlog.shuffle_mutables.value select_unfrozen = dlog.use_unfrozen.value mutate_list_ui = getlist(dlog.mutate_list.value) polar_aas = dlog.aa_list.value uerror = CheckAAs ( polar_aas ) sidechain_minimum = dlog.sidechain_minimum.value first_qs_slot = dlog.start_qs_slot.value last_qs_slot = dlog.end_qs_slot.value if first_qs_slot > last_qs_slot then uerror = "first quick save " .. first_qs_slot .. " greater than last quick save " .. last_qs_slot end -- -- these are Should Not Occur Errors (SNOCR), since the dialog uses a slider... -- if first_qs_slot < 1 or first_qs_slot > 99 then uerror = "first quick save " .. first_qs_slot .. " invalid, valid range 1-99" end if last_qs_slot < 1 or last_qs_slot > 99 then uerror = "last quick save " .. last_qs_slot .. " invalid, valid range 1-99" end VERBOSE = dlog.VERBOSE.value end until rc < 0 or uerror:len () == 0 if rc > 0 then return true else return false end end -- -- just being thorough here -- function CheckAAs ( aalist ) local err = "" for ii = 1, aalist:len () do local aac = aalist:sub ( ii, ii ) local aar = AminoAcids [ aac ] if aar == nil then err = "invalid amino acid \"" .. aac .. "\" specified" break else if not aar.polar then err = "amino acid \"" .. aac .. "\" is not polar" break end end end return err end function NoMutablesFound () local ask = dialog.CreateDialog ( ReVersion .. " no mutables found" ) ask.l15 = dialog.AddLabel ( "No mutable segments found in this puzzle." ) ask.l20 = dialog.AddLabel ( "This recipe only works on design puzzles," ) ask.l30 = dialog.AddLabel ( "which have segments that can be mutated (mutable segments)." ) ask.l35 = dialog.AddLabel ( "Also, a \"Hydrogen Bond Network\" condition is required." ) ask.OK = dialog.AddButton ( "OK", 1 ) dialog.Show ( ask ) end function NoMutablesSpecd () local ask = dialog.CreateDialog ( ReVersion .. " no mutables specified" ) ask.l15 = dialog.AddLabel ( "No mutable segments specified." ) ask.l20 = dialog.AddLabel ( "You can specify which segments to mutate in two ways:" ) ask.l25 = dialog.AddLabel ( "1) freeze all, then unfreeze the sidechains you want to mutate" ) ask.l35 = dialog.AddLabel ( "2) specifying numeric ranges in the segment ranges box" ) ask.OK = dialog.AddButton ( "OK", 1 ) dialog.Show ( ask ) end function NoHbondFilter () local ask = dialog.CreateDialog ( ReVersion .. " no Hydrogen Bond Network condition" ) ask.l15 = dialog.AddLabel ( "This puzzle doesn't have a condition (filter)" ) ask.l20 = dialog.AddLabel ( "called \"Hydrogen Bond Network\"." ) ask.l25 = dialog.AddLabel ( "This recipe only works on puzzles with this condition." ) ask.OK = dialog.AddButton ( "OK", 1 ) dialog.Show ( ask ) end function main () Ident ( ReVersion ) -- -- initialize -- seedRandom () n_residues = structure.GetCount () n_mutables = 0 for ii = 1, n_residues do if structure.IsMutable ( ii ) then n_mutables = n_mutables + 1 end end if n_mutables == 0 then NoMutablesFound () cleanup () -- cleanup is just a regular function call here return -- we still need to leave once cleanup is done end filter.EnableAll () init_score = GetScore () print ( "initial score = " .. r3 ( init_score ) ) best_score = init_score local hbond_filter = false filter_list = filter.GetNames () print ( #filter_list .. " Filters" ) for ii = 1, #filter_list do local filt = { name = "", hasbonus = false, initbonus = 0, currbonus = 0, initsatisfied = false, currsatisfied = false, } filt.name = filter_list [ ii ] if not filter.IsEnabled ( filt.name ) then filter.Enable ( filt.name ) end filt.hasbonus = filter.HasBonus ( filt.name ) if filt.hasbonus then filt.initbonus = filter.GetBonus ( filt.name ) end filt.initsatisfied = filter.ConditionSatisfied ( filt.name ) filter_stat [ #filter_stat + 1 ] = filt local filtinfo = "" if filt.hasbonus then filtinfo = filtinfo .. "score = " .. r3 ( filt.initbonus ) else if filt.initsatisfied then filtinfo = filtinfo .. "satsified" else filtinfo = filtinfo .. "not satisfied" end end if filt.name == "Hydrogen Bond Network" then hbond_filter = true filter.Enable ( filt.name ) print ( "Enabling " .. filt.name .. ", " .. filtinfo ) else filter.Disable ( filt.name ) print ( "Disabling " .. filt.name .. ", " .. filtinfo ) end end if not hbond_filter then NoHbondFilter () cleanup () return end init_filter_score = GetFilterScore () print ( "initial hydrogen bond network score " .. r3 ( init_filter_score ) ) band.DisableAll () undo.SetUndo ( false ) save.Quicksave ( start_slot ) if GetParameters () == false then cleanup () return -- graceful exit end -- -- Lua method calls can be used where available -- in this case, polar_aa:len() is the method version, -- equivalent to string.len ( polar_aas ) -- n_polar_aas = polar_aas:len() if select_unfrozen == true then GetListOfMutablesUnfrozen () else GetListOfMutablesSelected () end PrintParameters () if #mutable_list == 0 then NoMutablesSpecd () cleanup () return end print ( "--" ) next_qs_slot = first_qs_slot Init () local mutidx = 0 if shuffle_mutables then mutable_list = ShuffleTable ( mutable_list ) end while next_qs_slot <= last_qs_slot do n_it = n_it + 1 if VERBOSE then print ( "begin run " .. n_it ) end -- -- shuffle the mutables each time, process each entry once per iteration -- if shuffle_mutables then mutidx = mutidx + 1 if mutidx > #mutable_list then mutidx = 0 end if mutidx == 0 then mutable_list = ShuffleTable ( mutable_list ) mutidx = 1 end -- -- else pick a random mutable, can repeat the same segment multiple times -- else mutidx = math.random ( #mutable_list ) end local mutate_seg = mutable_list [ mutidx ] if VERBOSE then print ( "run " .. n_it .. ", probing segment " .. mutate_seg ) end local nn = 0 local valid_replacement_found = false local replacement_aa = "" -- a bit untidy this code repeat nn = nn + 1 local kk = math.random ( n_polar_aas ) replacement_aa = string.sub ( polar_aas, kk, kk ) if structure.CanMutate ( mutate_seg, replacement_aa ) == true then structure.SetAminoAcid ( mutate_seg, replacement_aa ) sidechain_score = current.GetSegmentEnergySubscore ( mutate_seg , "Sidechain" ) if VERBOSE then print ( "mutation " .. nn .. " for segment " .. mutate_seg .. " to \"" .. replacement_aa .. "\", sidechain score " .. r3 ( sidechain_score ) ) end if sidechain_score > sidechain_minimum then valid_replacement_found = true end end until nn > 5 or valid_replacement_found == true selection.SelectAll () --structure.ShakeSidechainsSelected ( 1 ) structure.WiggleSelected ( 2 * WF, false, true ) -- all selected, but wiggle sidechains only filter_score = GetFilterScore () if filter_score > init_filter_score then print ( "run " .. n_it .. ", segment " .. mutate_seg .. ", mutated to \"" .. replacement_aa .. "\"" ) print ( "hydrogen bond network score = " .. filter_score .. ", gain = " .. r3 ( filter_score - init_filter_score ) ) -- -- check overall score and all filters, report on changes -- filter.EnableAll () local curr_score = GetScore () print ( "current score = " .. r3 ( curr_score ) .. ", change = " .. r3 ( curr_score - init_score ) ) for ii = 1, #filter_stat do local filt = filter_stat [ ii ] if filt.hasbonus then filt.currbonus = filter.GetBonus ( filt.name ) if filt.initbonus ~= filt.currbonus then print ( "filter \"" .. filt.name .. "\", current bonus = " .. r3 ( filt.currbonus ) .. ", change = " .. r3 ( filt.currbonus - filt.initbonus ) ) end end filt.currsatisfied = filter.ConditionSatisfied ( filt.name ) if filt.initsatisfied ~= filt.currsatisfied then print ( "filter \"" .. filt.name .. "\" satisfied, current = " .. tostring ( filt.currsatisfied ) .. ", initial = " .. tostring ( filt.initsatisfied ) ) end end -- -- only make saves if the current solution if the different than -- what we have -- local saveit = true for ii = 1, #slot_stat do if slot_stat [ ii ].score == curr_score then print ( "same score found in slot " .. slot_stat [ ii ].slot .. ", not saving" ) saveit = false break end end if saveit then print ( "storing in slot " .. next_qs_slot ) save.Quicksave ( next_qs_slot ) slot_stat [ #slot_stat + 1 ] = { slot = next_qs_slot, score = curr_score } next_qs_slot = next_qs_slot + 1 -- -- make a named save -- local solname = r3 ( curr_score ) .. ", HBN score = " .. r3 ( filter_score ) .. ", gain = " .. r3 ( filter_score - init_filter_score ) save.SaveSolution ( solname ) print ( "saved solution as \"" .. solname .. "\"" ) end -- -- reset filters -- for ii = 1, #filter_stat do local filt = filter_stat [ ii ] if filt.name == "Hydrogen Bond Network" then filter.Enable ( filt.name ) else filter.Disable ( filt.name ) end end Init () elseif VERBOSE then print ( "no gain, run " .. n_it .. ", segment " .. mutate_seg ) end -- -- let the user know something's happening -- if n_it % 100 == 0 then print ( "number of runs completed = " .. n_it ) end end -- while next_qs_slot <= last_qs_slot print ( "maximum quick save slot " .. last_qs_slot .. " reached, recipe complete" ) cleanup () end -- -- Ident - print identifying information at beginning and end of recipe -- -- slugline - first line to print - normally recipe name and version -- -- v0.2 - LociOiling - 20191118 -- + streamline the format -- + user.GetGroupName not working, remove -- v0.3 - LociOiling - 20201012 -- + combine user name and rank in one line -- + don't report on group at all -- function Ident ( slugline ) local function round ( ii ) return ii - ii % 0.001 end print ( slugline ) print ( "Puzzle: " .. puzzle.GetName () .. " (" .. puzzle.GetPuzzleID () .. ")" ) print ( "Track: " .. ui.GetTrackName () ) local luser = user.GetPlayerName () local scoretype = scoreboard.GetScoreType () local scort = "" if scoretype == 0 then scort = "soloist" elseif scoretype == 1 then scort = "evolver" elseif scoretype == 2 then scort = "all hands" elseif scoretype == 3 then scort = "no score" else scort = "unknown/error" end print ( "User: " .. luser .. " (" .. scort .. " #" .. scoreboard.GetRank ( scoretype ) .. ")" ) end function cleanup ( errmsg ) if CLEANUPENTRY ~= nil then return end CLEANUPENTRY = true print ( "---" ) local reason local start, stop, line, msg if errmsg == nil then reason = "complete" else -- -- civilized error reporting, thanks to Bruno K. and Jean-Bob -- start, stop, line, msg = errmsg:find ( ":(%d+):%s()" ) if msg ~= nil then errmsg = errmsg:sub ( msg, #errmsg ) end if errmsg:find ( "Cancelled" ) ~= nil then reason = "cancelled" else reason = "error" end end Ident ( ReVersion .. " " .. reason ) if reason == "error" then print ( "Unexpected error detected" ) print ( "Error line: " .. line ) print ( "Error: \"" .. errmsg .. "\"" ) end print ( "number of runs " .. n_it ) undo.SetUndo ( true ) if next_qs_slot > first_qs_slot then print ( "Loading quick saves " .. first_qs_slot .. " to " .. next_qs_slot - 1 ) for ii = first_qs_slot , next_qs_slot - 1 do save.Quickload ( ii ) end else print ( "No gain, restoring starting pose" ) save.Quickload ( start_slot ) end band.EnableAll () filter.EnableAll () local final_score = GetScore () print ( "final score = " .. r3 ( final_score ) ) print ( "gain = " .. r3 ( final_score - init_score ) ) for ii = 1, #filter_stat do local filt = filter_stat [ ii ] if filt.hasbonus then filt.currbonus = filter.GetBonus ( filt.name ) if filt.initbonus ~= filt.currbonus then print ( "filter \"" .. filt.name .. "\", current bonus = " .. r3 ( filt.currbonus ) .. ", change = " .. r3 ( filt.currbonus - filt.initbonus ) ) end end filt.currsatisfied = filter.ConditionSatisfied ( filt.name ) if filt.initsatisfied ~= filt.currsatisfied then print ( "filter \"" .. filt.name .. "\" satisfied, current = " .. tostring ( filt.currsatisfied ) .. ", initial = " .. tostring ( filt.initsatisfied ) ) end end end xpcall ( main, cleanup )

Comments


LociOiling Lv 1

HNetwork Probe 2.2 has new features and some changes meant to make it easier to understand what's going on. See the comments for HNetwork Probe 2.0 for the basics of how the recipe works.

In the expanded settings dialog, there's a new "wiggle factor" setting that's similar to the ones found in EDRW/DRemixW. As before, for WF = 1, the recipe wiggles all sidechains for two cycles after a mutation. With WF = 2, it's four cycles, WF = 3 gives you six cycles, and so on.

There are also two new checkboxes. The recipe normally tries to mutate all selected mutable segments at the beginning of each run. "Skip initial mutates" causes the recipe to skip this step.

The recipe previously picked a segment at random from the list of selected mutable segments. This meant the same segment could be picked two or more times in a row. Checking "shuffle mutable list" means the recipe first shuffles the list of mutables, then processes the list in its new order. If you have ten mutable segments selected, all ten will be tried before the list is shuffled again.

On the more cosmetic side, the dialog tries to make it clearer that the default "select unfrozen" option means working on mutable segments with unfrozen sidechains. If this option is unchecked, you must specify segment ranges in the "use ranges" textbox.

The list of amino acids specified is now validated, and an error message displayed for invalid entries. An error message is also displayed if the start quick save slot is greater than the end quick save slot.

A dialog box now appears if run the recipe for a non-design puzzle or a puzzle without a hydrogen bond network condition.

The recipe now puts out a message every 100 runs just to reassure the anxious user. The new "verbose output" checkbox will produce lots more output if checked.

The recipe now includes an improved random number seed (hard to be sure on that), and an expanded cleanup routine which displays line numbers for script errors.

There are also a number of small stylistic changes to the code.

This version of the recipe includes code from Karen_CH, Timo van der Laan, rav3n_pl, Bruno Kestemont, and others.

LociOiling Lv 1

HNetwork Probe 2.3 adds named saves when the hydrogen bond network score improves. The saves are listed in the scriptlog when they happen, and can be seen in Open/Share Solutions (control - o).

The named saves are in addition to the original save logic, which relies on quick save slots. One advantage of named saves is that they are easier to find if the client crashes.

The named saves are identified by the overall score, followed by the hydrogen bond network score and the change in the h-bond score.

Version 2.3 includes several other changes related to saves. When the hydrogen bond network score improves, the recipe now calculates the total score, including all the conditions/filters. The details are reported in the scriptlog. If the new solution has the same total score as one already saved, it's considered a duplicate, and not saved. Otherwise, the solution is saved in a quick save slot, and in a named save.

As in previous versions, the original pose is stored in quick save slot 1. In version 2.3, the quick save sliders now protect slot 1. If there's no gain when the recipe ends, the original pose is now restored.

At the end, the recipe now turns on all filters. As before, it restores the quick save slots used, in order. The final pose is the one in the last quick save slot, showing the score with all filters on. This may or may not be the best pose.