Icon representing a recipe

Recipe: Region Scorer V 1.0.1 -- Brow42

created by brow42

Profile


Name
Region Scorer V 1.0.1 -- Brow42
ID
38634
Shared with
Public
Parent
None
Children
None
Created on
February 24, 2012 at 05:31 AM UTC
Updated on
February 24, 2012 at 05:31 AM UTC
Description

Like a text version of relative score coloring.Ranks all segment scores from 0 to N-1. Can use custom scoring. Can loop through all starts. Max CI = current slider.

Best for


Code


-- --[[ * Region Scorer * Original Author brow42 * Version 1.0.1 2/23/2012 brow42 * Bug Fix: Custom score was computed wrong for printing. Ranking was unaffected. * Now prints puzzle name. * Version 1.0 2/14/2012 * This is like a printed version of relative score coloring. * It ranks every segment from 0 to N-1, where N is the user * selectable number of groups. If you have a multistart puzzle, * it will rank each start, after an optional stabilization * procedure. CI during stabilization will never exceed the * CI behavior slider value at the time you start the recipe. * Any or all subscores may be used in the ranking. Ranking * is shown first as a fixed-width list for visual comparison, * and then as ranges. Segment scores are averaged over an * adjustable sliding window. * * The intent of this recipe is to help you identify the worst * parts for work, or the best and worst parts of each start * for partial threading. --]] -- ================================== Begin Options and Globals options = { multistart = false, wiggle = false, qstab = false, smoothing = 3, quantiles = 5, minlen = 1, keepbest = true, keepworst = true } options.score = { all = true } -- overrides checking individual scores version = '1.0' title = 'Region Scorer v. '..version subscores = { 'clashing', 'packing', 'hiding', 'bonding', 'backbone', 'sidechain', 'disulfides', 'reference', 'other' } for i=1,#subscores do options.score[subscores[i]] = true end -- qstab options qstab = { max_ci = behavior.GetClashImportance(), min_ci = 0.1, early_stop = 0.001 -- don't do more iterations if < this } -- ================================== End Options and Globals -- ================================== Begin New Dialog Library Functions -- Trap a crasher dialog._CreateDialog = dialog.CreateDialog dialog.CreateDialog = function(x) if x == nil or type(x) ~= 'string' then error('Bad arg 1 to CreateDialog() (expected string)') end return dialog._CreateDialog(x) end --[[ Throws up a dialog containing just text, provided as a string or table of strings in the first argument. Title and buttons are optional. Return value is 1 if the first button is clicked and 0 if the second button is clicked or the window is closed. --]] function dialog.MessageBox(msg,title,buttontext1, buttontext0) title = title or '' local d = dialog.CreateDialog(title) if type(msg) == 'string' then d['1'] = dialog.AddLabel(msg) else for i = 1,#msg do d[tostring(i)] = dialog.AddLabel(msg[i]) end end buttontext1 = buttontext1 or 'Ok' d.button = dialog.AddButton(buttontext1,1) if buttontext0 then d.button0 = dialog.AddButton(buttontext0,0) end return dialog.Show(d) end --[[ Add items to the dialog from a table in the order given. The each table entry is the dialog control function, the control label, the data key name, and the control options. If this function fails, it gives an error saying which table item it failed on. d = a created dialog, fields = table of dialog controls as described, object = data table --]] function dialog.AddFromTable(d,fields,object) local func, label, id, value -- renamed field parameters for clarity -- function wrappers to catch errors, explicitly named for clarity function _AddLabel(args) d[id] = dialog.AddLabel(label) end function _AddCheckbox(args) d[id] = dialog.AddCheckbox(label,value) end function _AddSlider(args) d[id] = dialog.AddSlider(label,value,args[4],args[5],args[6]) end function _AddTextbox(args) d[id] = dialog.AddTextbox(label,value) end for i = 1, #fields do local rc = true local err rc,err = pcall( function() func,label,id = fields[i][1],fields[i][2],fields[i][3] end) value = object[id] if func == dialog.AddLabel then id='_AL'..tostring(i) rc,err = pcall(_AddLabel,fields[i]) else -- this is a crasher for AddTextbox but not an error for AddLabel! if value == nil then error('Missing data field for field #'..tostring(i)..'\n(Did you forget to pass a string?)') end if func == dialog.AddCheckbox then rc,err = pcall(_AddCheckbox,fields[i]) elseif func == dialog.AddSlider then rc,err = pcall(_AddSlider,fields[i]) elseif func == dialog.AddTextbox then rc,err = pcall(_AddTextbox,fields[i]) else error('Unknown control in dialog field #'..tostring(i)) end end -- if AddLabel if rc == false then error('Error setting dialog field #'..tostring(i)..(err and ('\n'..err) or '')) end end end --[[ This just copies the data from the dialog to the data table. --]] function dialog.ReadFields(d,fields,object) for i = 1,#fields do if fields[i][1]~= dialog.AddLabel then object[fields[i][3]] = d[fields[i][3]].value end end end -- ================================== End New Dialog Library Functions -- ================================== Begin Dialogs function DoAboutDialog() dialog.MessageBox({ 'This divides the protein segment scores into', 'up to 10 groups, like a printable relative', 'score coloring. This let\'s you find the best', 'and worst parts.', '', 'If you check "Multistart" then it will cycle', 'through all the starts, optionally doing a', 'wiggle or full stabilization of each start.', '(Your start # and structure will be restored.)', '', 'Smoothing averages scores over "X" neighbors', 'to each side. Advanced scoring let\'s you', 'pick score parts to score on.' }, 'About '..title) end function DoAdvScoreDialog() local d = dialog.CreateDialog('Advanced Scoring') d.label = dialog.AddLabel('Select one or more:') for i =1,#subscores do d[subscores[i]] = dialog.AddCheckbox(subscores[i], options.score.all or options.score[subscores[i]]) end d.okay = dialog.AddButton('Accept',1) d.cancel = dialog.AddButton('Cancel',0) d.clear = dialog.AddButton('Clear', 2) d.all = dialog.AddButton('All', 3) local ntrue while true do -- exits on cancel or valid accept local rc = dialog.Show(d) if rc == 0 then return -- exit no changes elseif rc == 1 then ntrue = 0 for i = 1,#subscores do ntrue = d[subscores[i]].value and ntrue + 1 or ntrue end if ntrue > 0 then break end -- accept only if at least 1 score field elseif rc == 2 then for i = 1,#subscores do d[subscores[i]].value = false end else for i = 1,#subscores do d[subscores[i]].value = true end end end -- save changes for i = 1,#subscores do options.score[subscores[i]] = d[subscores[i]].value end options.score.all = ntrue == #subscores end function DoMainDialog() local max_smooth = 10 > structure.GetCount() -1 and structure.GetCount() -1 or 10 local main = { { dialog.AddLabel, 'Check this to analyze all starts' }, { dialog.AddCheckbox, 'Multistart', 'multistart'}, { dialog.AddCheckbox, ' Do a wiggle (good starts)', 'wiggle'}, { dialog.AddCheckbox, ' Do a full band/wiggle/qstab (low starts)', 'qstab' }, { dialog.AddSlider, 'Smoothing', 'smoothing', 0, max_smooth, 0}, { dialog.AddSlider,'Quantiles', 'quantiles', 1, 10, 0}, { dialog.AddLabel, 'Check these to ony show best and worst parts' }, { dialog.AddCheckbox, 'Ignore less-than-best', 'keepbest'}, { dialog.AddCheckbox, 'Ignore better-than-worst', 'keepworst'} } local d = dialog.CreateDialog(title) dialog.AddFromTable(d,main,options) d.okay = dialog.AddButton('Okay',1) d.cancel = dialog.AddButton('Cancel',0) d.adv = dialog.AddButton('Adv. Scoring', 2) d.help = dialog.AddButton('About',3) repeat local rc = dialog.Show(d) if rc ~= 0 then dialog.ReadFields(d,main,options) end if rc == 2 then DoAdvScoreDialog() elseif rc == 3 then DoAboutDialog() end until rc == 1 or rc == 0 return rc end -- ================================== End Dialogs -- ================================== Begin Utility Functions function PrintTable(tab,indent,seen) indent = indent or '' if tab == nil or type(tab) ~= 'table' then return end seen = seen or {} if seen[tab] then print(indent..'<cycle or dupe>') return end seen[tab] = true for i,v in pairs(tab) do if type(v) == 'table' then print(indent,i) PrintTable(v,indent..' ',seen) else print(indent,i,v) end end end function puzzle.GetCount() puzzle.StartOver() local first_score = first_score or current.GetScore() local i = 0 repeat puzzle.StartOver() i = i + 1 until current.GetScore() == first_score return i end function GetScoreArray() local s = {} local min, max min = math.huge max = - min for i = 1, structure.GetCount() do local e = GetSegmentScore(i) s[#s+1] = e min = e < min and e or min max = e > max and e or max end return s, min, max end function MinMax(tab) local min, max min = math.huge max = - min for i = 1, #tab do local e = tab[i] min = e < min and e or min max = e > max and e or max end return min, max end function Smooth(list,w) result = {} if w > #list -1 then w = #list - 1 end -- max offset reflects to other endpoint for i = 1,#list do for j = -w,w do k = i + j if k < 1 then k = 1 + (1-k) elseif k > #list then k = #list - (k - #list) end result[i] = (result[i] or 0) + list[k] end end local n = 2 * w + 1 for i = 1,#result do result[i] = result[i] / n end return result end function Quantize(tab, n, min1, max1) local min,max = min1, max1 if min == nil or max == nil then min1, max1 = MinMax(tab) if min == nil then min = min1 end if max == nil then max = max1 end end local q = (max - min) / n if q == 0 then q = math.huge end local result = {} for i = 1,#tab do local v = math.floor((tab[i] - min)/ q) if v >= n then v = n-1 end result[i] = v end if options.keepbest or options.keepworst then for i = 1,#result do local keep = false if result[i] == n-1 and options.keepbest then keep = true elseif result[i] == 0 and options.keepworst then keep = true end result[i] = keep and result[i] or '_' end end return result end -- identify ranges with the same value function Summarize(ss) local c = '' local count = 0 local result = {} for i = 1,#ss+1 do if ss[i] == c then count = count + 1 else if count > 0 then local x = ', ' if result[c] == nil then result[c] = '' x='' end if count == 1 then result[c] = result[c]..string.format('%s%d',x,i-1) else result[c]= result[c]..string.format('%s%d-%d',x,i-count,i-1) end end count = 1 c = ss[i] end end local resultstr = '' for i = 1,options.quantiles do if result[i-1] ~= nil then resultstr = resultstr .. string.format('%d: ',i-1) .. result[i-1] .. (i < options.quantiles and '; ' or '') end end return resultstr end -- Customizable score function GetScore() if options.score.all == true then return current.GetEnergyScore() end tmp = {} for i = 1, #subscores do if options.score[subscores[i]] then tmp[#tmp+1] = subscores[i] end end local e = 0 for j = 1, structure.GetCount() do for i = 1,#tmp do e = tmp[i] and current.GetSegmentEnergySubscore(j,tmp[i]) + e end end return e end function GetSegmentScore(iSeg) if options.score.all == true then return current.GetSegmentEnergyScore(iSeg) end local e = 0 for i = 1,#subscores do if options.score[subscores[i]] then e = e + current.GetSegmentEnergySubscore(iSeg,subscores[i]) end end return e end function GetScoreForPrinting() -- print energy and custom score if options.score.all then return string.format('%f',current.GetEnergyScore()) end return string.format('%f (%f)',current.GetEnergyScore(),GetScore()) end -- ================================== End Utility Functions -- ================================== Begin QStab Functions -- set the clash between min and max but no less than min regardless of max function qstab.CI(x) x = x > 1 and 1 or x x = x < 0 and 0 or x x = x * (qstab.max_ci - qstab.min_ci) x = x < qstab.min_ci and qstab.min_ci or x return x end -- call functin f n times unless nothing is happening function qstab.ws(n,stabfunc) local e1, e2 while n > 0 do e1 = GetScore() stabfunc(1) e2 = GetScore() if math.abs(e1-e2) < qstab.early_stop then break end n = n - 1 end end function qstab.WA(n) qstab.ws(n, structure.WiggleAll) end function qstab.SS(n) qstab.ws(n, structure.ShakeSidechainsAll) end function qstab.BandAll() local len = 0.001 local n = structure.GetCount() band.DeleteAll() for i = 2,n-1 do band.Add(i,i-1,i+1,0.001,0,0) end band.Add(1,2,3,len,0,0) band.Add(n,n-1,n-2,len,0,0) for i = 1,band.GetCount() do band.SetGoalLength(i,len) band.SetStrength(i,3.0) end end function QStab() qstab.BandAll() qstab.CI(1.0) qstab.SS(5) qstab.CI(0.1) qstab.WA(5) band.DeleteAll() qstab.CI(0.4) qstab.WA(1) qstab.CI(1.0) qstab.SS(1) qstab.WA(5) end -- ================================== End QStab Functions -- ================================== Begin Main if DoMainDialog() == 0 then return end print(puzzle.GetName()) if options.score.all == false then print('Custom Scoring:') PrintTable(options.score, ' ') end qslot = 1 if options.multistart then print('Saving puzzle to slot',qslot) save.Quicksave(qslot) puzzle.StartOver() print('Individual start analyses (independent scaling)') if options.qstab or options.wiggle then print('Max. CI for this run is',qstab.max_ci) end end first_score = current.GetEnergyScore() iStart = 1 results = {} -- to print at the end repeat local s1, s2 = GetScoreForPrinting(), '' -- scores for printing -- stabilize multistarts if options.multistart then if options.qstab then QStab() s2 = 'QStab: '..tostring(GetScoreForPrinting()) elseif options.wiggle then qstab.WA(10) s2 = 'Wiggle: '..tostring(GetScoreForPrinting()) end end scores = GetScoreArray() smoothed = Smooth(scores,options.smoothing) local min,max = MinMax(smoothed) quantized = Quantize(smoothed,options.quantiles,min,max) summary = Summarize(quantized) print('Start #',iStart,s1,s2) print('Segment Rank:',table.concat(quantized,'')) print('Summary:',summary) -- save results results[iStart] = { s1 = s1, s2 = s2, scores = scores, smoothed = smoothed, quantized = quantized, min = min, max = max } -- Move on to the next start if options.multistart then puzzle.StartOver() iStart = iStart + 1 end until current.GetEnergyScore() == first_score -- istart contains # starts + 1 (assuming no duplicate scores) if options.multistart then print('Looping through to return to original start...') for i=1,iStart-2 do puzzle.StartOver() end print('Restoring saved puzzle.') save.Quickload(qslot) end -- print multistart summary if options.multistart then local min, max = math.huge,-math.huge for i = 1,#results do if results[i].min < min or i == 1 then min = results[i].min end if results[i].max > max or i == 1 then max = results[i].max end end print('Combined start summary (uniform scaling)') for i = 1,#results do local blah = i == # results and '<- this is where you started' or '' print('Start:',i,results[i].s1,results[i].s2,blah) end print('Segment Rankings:') for i = 1,#results do quantized = Quantize(results[i].smoothed,options.quantiles,min,max) print('Start:',string.format('%2d',i),table.concat(quantized,'')) results[i].quantized2 = quantized end print('Summaries:') for i = 1,#results do summary = Summarize(results[i].quantized2) print('Start:',i,summary) end end

Comments


brow42 Lv 1

This is like a printed version of relative score coloring. It ranks every segment from 0 to N-1, where N is the user selectable number of groups. If you have a multistart puzzle, it will rank each start, after an optional stabilization procedure. CI during stabilization will never exceed the CI behavior slider value at the time you start the recipe. Any or all subscores may be used in the ranking. Ranking is shown first as a fixed-width list for visual comparison, and then as ranges. Segment scores are averaged over an adjustable sliding window.

The intent of this recipe is to help you identify the worst parts for work, or the best and worst parts of each start for partial threading.

A lot of experimentation is needed to see if this works. For partial threading, I suggest only combining the ends, or very long high-scoring sections.