Code
-- Copyright notice.
-- You may copy and paste at will. Plagiarize shamelessly!
-- End copyright notice
-- Mostly user-configurable globals
remix_length = 4 -- Length of sections to remix.
number_of_remixes = 4 -- It'll look for the best score from this number of remixes.
ss_criterion = 4 -- Controls whether to remix a section based on secondary structure
wiggle_with_filters_off = true -- for speedup
test_for_stability = true
keep_bands = true
min_residue = 1
max_residue = 999
inner_sphere = {}
outer_sphere = {}
kInnerSphereRadius = 10
kOuterSphereRadius = 16
kDefaultWiggleDuration = 12
kLowCiWiggle = 0.5
score_type = 1
verbose = true
local_mutate_threshold = 80
-- Use of quicksave slots
kOriginalStructureOrNewBest = 1
kTemp = 2
kRemixQuicksaveStart = 3
-- Globals
original_secondary_structure = {}
fixed_residues = {}
n_residues = 0
best_score = 0
filter_list = {}
-- For monitoring
loss_from_low_ci_wiggle = {}
original_residues = {}
new_residues = {}
start_time = 0
scores = {}
ids = {}
-- Amino acids. Hydrophobics first.
single_letter_codes = { "g","a","v","l","i","m","f","w","p","s","t","c","y","n","q","d","e","k","r","h"}
three_letter_codes = { "Gly","Ala","Val","Leu","Ile","Met","Phe","Trp","Pro","Ser","Thr","Cys","Tyr","Asn","Gln","Asp","Glu","Lys","Arg","His"}
-- Begin
function r3 ( x )
-- Round to 3 decimal places
t = 10 ^ 3
return math.floor ( x*t + 0.5 ) / t
end
function r1 ( x )
-- Round to 1 decimal place
t = 10 ^ 1
return math.floor ( x*t + 0.5 ) / t
end
function GetScore ()
if ( score_type == 1 ) then
score = current.GetScore ()
end
return score
end
function aa1_to_aa3 ( k )
for j = 1, 20 do
if ( k == single_letter_codes [ j ] ) then
return three_letter_codes [ j ]
end
end
return "Unk"
end
function Coprime ( n )
local primes = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101}
i = #primes
-- find the highest prime < 70% of the residue count and which is coprime with the number of residues
while ( i >= 1 ) do
if ( primes [ i ] < n*0.7 and n % primes [ i ] ~= 0 ) then
return primes [ i ]
end
i = i - 1
end
return 1
end
function GetFilterList ()
filter_list = filter.GetEnabledNames ()
end
function TurnFiltersOn ()
for i = 1 , #filter_list do
filter.Enable ( filter_list [ i ] )
end
end
function TurnFiltersOff ()
for i = 1 , #filter_list do
filter.Disable ( filter_list [ i ] )
end
end
function SegmentComposition ( first , last )
-- Returns number of residues of each secondary structure type
n_loop_residues = 0
n_helix_residues = 0
n_sheet_residues = 0
for i = first , last do
ss = original_secondary_structure [ i ]
if ( ss == "L" ) then
n_loop_residues = n_loop_residues + 1
elseif ( ss == "H" ) then
n_helix_residues = n_helix_residues + 1
elseif ( ss == "E" ) then
n_sheet_residues = n_sheet_residues + 1
end
end
return n_loop_residues , n_helix_residues , n_sheet_residues
end
function IsSegmentRemixable ( first , last )
-- Remixable in the sense that the secondary structure satisfies the specified criteria
n_loop_residues , n_helix_residues , n_sheet_residues = SegmentComposition ( first , last )
if ( ss_criterion == 1 ) then
-- Loops only
if ( n_helix_residues > 0 or n_sheet_residues > 0 ) then
return false
else
return true
end
elseif ( ss_criterion == 2 ) then
-- Loops with one non-loop residue
if ( n_helix_residues + n_sheet_residues > 1 ) then
return false
else
return true
end
elseif ( ss_criterion == 3 ) then
-- Loops or sheets
if ( n_helix_residues > 0 ) then
return false
else
return true
end
elseif ( ss_criterion == 4 ) then
return true
end
end
function pseudorandom ( score )
-- Returns a pseudorandom number >= 0 and < 1.
-- Based on the fractional part of the score
if ( score >= 0 ) then
return score % 1
else
return (-score ) % 1
end
end
function GetListOfFixedResidues ()
for i = 1 , n_residues do
fixed_residues [ i ] = false
end
for i = 1 , n_residues do
seg_backbone_frozen, seg_sidechain_frozen = freeze.IsFrozen ( i )
if ( seg_backbone_frozen == true ) then
fixed_residues [ i ] = true
end
s = structure.IsLocked ( i )
if ( s == true ) then
fixed_residues [ i ] = true
end
end
end
function DoesSectionContainFixedResidues ( start_idx , end_idx )
for i = start_idx , end_idx do
if ( fixed_residues [ i ] == true ) then
return true
end
end
return false
end
function ShakeLowCIWiggle ( c_score )
save.Quicksave ( kTemp )
behavior.SetClashImportance ( 0.10 )
structure.ShakeSidechainsSelected ( 1 )
behavior.SetClashImportance ( 1 )
structure.WiggleSelected ( kDefaultWiggleDuration )
score = GetScore ()
if ( score < c_score ) then
save.Quickload ( kTemp )
score = c_score
end
end
function Nudge ( c_score )
save.Quicksave ( kTemp )
behavior.SetClashImportance ( kLowCiWiggle )
structure.WiggleSelected ( 1 )
table.insert ( loss_from_low_ci_wiggle , c_score - GetScore () )
behavior.SetClashImportance ( 1 )
structure.WiggleSelected ( kDefaultWiggleDuration )
score = GetScore ()
if ( score < c_score ) then
save.Quickload ( kTemp )
end
end
function RecordImprovement ()
best_score = GetScore ()
-- save.LoadSecondaryStructure ()
save.Quicksave ( kOriginalStructureOrNewBest )
print ( "Improvement to ".. r3 ( best_score ) )
end
function ConstructListOfResiduesWithinInnerSphere ( start_idx , end_idx )
for i = 1 , n_residues do
inner_sphere [ i ] = false
end
for i = 1 , n_residues do
for j = start_idx , end_idx do
if ( structure.GetDistance ( i , j ) < kInnerSphereRadius ) then
inner_sphere [ i ] = true
end
end
end
n = 0
for i = 1 , n_residues do
if ( inner_sphere [ i ] == true ) then
n = n + 1
end
end
return n
end
function SelectInnerSphere ()
selection.DeselectAll ()
for i = 1 , n_residues do
if ( inner_sphere [ i ] == true ) then
selection.Select ( i )
end
end
end
function ConstructListOfResiduesWithinOuterSphere ( start_idx , end_idx )
for i = 1 , n_residues do
outer_sphere [ i ] = false
end
for i = 1 , n_residues do
for j = start_idx , end_idx do
if ( structure.GetDistance ( i , j ) < kOuterSphereRadius ) then
outer_sphere [ i ] = true
end
end
end
n = 0
for i = 1 , n_residues do
if ( outer_sphere [ i ] == true ) then
n = n + 1
end
end
return n
end
function SelectOuterSphere ()
selection.DeselectAll ()
for i = 1 , n_residues do
if ( outer_sphere [ i ] == true ) then
selection.Select ( i )
end
end
end
function RemixPreamble ( start_idx , end_idx )
save.Quickload ( kOriginalStructureOrNewBest )
selection.DeselectAll ()
selection.SelectRange ( start_idx , end_idx )
end
function ShellSort ()
-- Adapted from Numerical Recipes in C
inc = 1
repeat
inc = inc * 3 + 1
until inc > n_residues
repeat
inc = inc / 3
inc = inc - inc % 1
for i = inc + 1 , n_residues do
v = scores [ i ]
w = ids [ i ]
j = i
flag = false
while ( flag == false and scores [ j - inc ] > v ) do
scores [ j ] = scores [ j - inc ]
ids [ j ] = ids [ j - inc ]
j = j - inc
if ( j <= inc ) then
flag = true
end
end
scores [ j ] = v
ids [ j ] = w
end
until inc <= 1
end
function TestForStability ()
-- Slightly inelegant this
if ( score_after_local_mutate > best_score ) then
print ( " Testing for stability" )
print ( " score with filters " .. r1 ( score_after_local_mutate ) )
filter.DisableAll ()
s1 = GetScore ()
structure.WiggleSelected ( kDefaultWiggleDuration )
s2 = GetScore ()
filter.EnableAll ()
score_after_local_mutate = GetScore ()
print ( " score without filters " .. r1 ( score_after_local_mutate ) )
if ( score_after_local_mutate < best_score ) then
print ( " Improvement rejected" )
end
end
end
function LocalMutate ( idx )
local score_after_w
local start_score = GetScore ()
save.Quicksave ( kTemp )
for j = 1 , n_residues do
scores [ j ] = structure.GetDistance ( idx , j )
ids [ j ] = j
end
ShellSort ()
selection.DeselectAll ()
for j = 1 , 10 do
selection.Select ( ids [ j ] )
end
behavior.SetClashImportance ( 0.09 )
structure.MutateSidechainsSelected ( 1 )
behavior.SetClashImportance ( 1.0 )
SelectOuterSphere ()
structure.WiggleSelected ( 15 )
score_after_w = GetScore ()
if ( score_after_w < start_score ) then
save.Quickload ( kTemp )
return start_score
else
return score_after_w
end
end
function RemixSeg ( start_idx , end_idx )
local i
local n_remixes
local n_inner
local n_outer
local nudge_ref
local local_mutate_ref
RemixPreamble ( start_idx , end_idx )
n_inner = ConstructListOfResiduesWithinInnerSphere ( start_idx , end_idx )
n_outer = ConstructListOfResiduesWithinOuterSphere ( start_idx , end_idx )
init_score = GetScore ()
if ( verbose ) then
print ( "init score : " .. r1 ( init_score ) .. " (" .. n_outer .. "," .. n_inner .. ")" )
end
n_remixes = structure.RemixSelected ( kRemixQuicksaveStart , number_of_remixes )
for i = 1 , n_remixes do
save.Quickload ( kRemixQuicksaveStart - 1 + i )
score_after_remix = GetScore ()
if ( math.abs ( score_after_remix - best_score ) >= 1e-5 ) then
SelectOuterSphere ()
if ( wiggle_with_filters_off == true ) then
TurnFiltersOff ()
end
structure.ShakeSidechainsSelected ( 1 )
if ( wiggle_with_filters_off == true ) then
TurnFiltersOn ()
end
score_after_shake = GetScore ()
if ( wiggle_with_filters_off == true ) then
TurnFiltersOff ()
end
structure.WiggleSelected ( kDefaultWiggleDuration )
if ( wiggle_with_filters_off == true ) then
TurnFiltersOn ()
end
score_after_wiggle = GetScore ()
SelectInnerSphere ()
structure.MutateSidechainsSelected ( 1 )
score_after_mutate = GetScore ()
gain_from_mutate = score_after_mutate - score_after_wiggle
SelectOuterSphere ()
structure.WiggleSelected ( kDefaultWiggleDuration )
score_after_2nd_wiggle = GetScore ()
if ( gain_from_mutate > 1 and math.abs ( score_after_2nd_wiggle - score_after_mutate ) < 1 ) then
Nudge ( score_after_2nd_wiggle )
score_after_nudge = GetScore ()
nudge_ref = r1 ( init_score - score_after_nudge )
else
score_after_nudge = score_after_2nd_wiggle
nudge_ref = "x"
end
if ( wiggle_with_filters_off == true ) then
-- filter.EnableAll ()
end
if ( init_score - score_after_nudge < local_mutate_threshold ) then
score_after_local_mutate = LocalMutate ( ( start_idx + end_idx ) / 2 )
local_mutate_ref = r1 ( init_score - score_after_local_mutate )
else
local_mutate_ref = "x"
score_after_local_mutate = -1e10
end
if ( verbose ) then
print ( "i : " .. i .. " " .. r1 ( init_score - score_after_remix ) ..
" " .. r1 ( init_score - score_after_shake ) ..
" " .. r1 ( init_score - score_after_wiggle ) ..
" " .. r1 ( init_score - score_after_mutate ) ..
" " .. r1 ( init_score - score_after_2nd_wiggle ) ..
" " .. nudge_ref ..
" " .. local_mutate_ref )
end
if ( test_for_stability == true ) then
TestForStability ()
end
if ( score_after_local_mutate > best_score ) then
RecordImprovement ()
return
end
end
end -- for i
end
function RemixAll ( )
local inc
max_possible_residue = max_residue - remix_length + 1
n_possible_segs = max_possible_residue - min_residue + 1
r = pseudorandom ( best_score )
start_idx = min_residue + n_possible_segs * r
start_idx = start_idx - start_idx % 1
inc = Coprime ( n_possible_segs )
-- The point of this Coprime, start_idx business is so we go through the protein in a
-- nonuniform way. Rather than remixing 1-4, 2-5 , 3-6 for example, we might do 21-24, 53-57, 3-6
-- or something like that
candidate_section_found = false
for i = 1 , n_possible_segs do
end_idx = start_idx + remix_length - 1
assert ( start_idx >= min_residue and end_idx <= max_residue , "Range error" )
if ( ( DoesSectionContainFixedResidues ( start_idx , end_idx ) == false ) and
( IsSegmentRemixable ( start_idx , end_idx ) == true ) ) then
candidate_section_found = true
rb = rb + 1
print ( "rb ".. rb .. " " .. start_idx .."-" .. end_idx .. " (" ..i .. "/" .. n_possible_segs .. ")" )
RemixSeg ( start_idx , end_idx )
end
start_idx = start_idx + inc
if ( start_idx > max_possible_residue ) then
start_idx = start_idx - n_possible_segs
end
end -- for i
if ( candidate_section_found == false ) then
print ( "No remixable sections found" )
exit (0)
end
end
function PrintParameters ()
print ( "Score type : Normal" )
print ( "Start score : " .. r3 ( best_score ) )
print ( "Remix length : " .. remix_length )
if ( ss_criterion == 1 ) then
print ( "Loops only" )
elseif ( ss_criterion == 2 ) then
print ( "Loops + 1" )
elseif ( ss_criterion == 3 ) then
print ( "Loops + sheets" )
elseif ( ss_criterion == 4 ) then
print ( "Any ss" )
end
print ( "Remix range " .. min_residue .. " to " .. max_residue )
print ( "N remixes " .. number_of_remixes )
print ( "Local mutate threshold " .. r1 ( local_mutate_threshold ) )
end
function GetOptionalParameters ()
local dlog = dialog.CreateDialog ( "Options" )
dlog.lm_threshold = dialog.AddSlider ( "Local mutate threshold" , local_mutate_threshold , 0 , 200 , 0 )
dlog.keep_bands = dialog.AddCheckbox ( "Keep bands enabled" , keep_bands )
dlog.wwfo = dialog.AddCheckbox ( "Filters off for wiggle" , wiggle_with_filters_off )
dlog.tfs = dialog.AddCheckbox ( "Test for stability" , test_for_stability )
dlog.verbose = dialog.AddCheckbox ( "Verbose" , verbose )
dlog.ok = dialog.AddButton ( "OK" , 1 )
dlog.cancel = dialog.AddButton ( "Cancel" , 0 )
if ( dialog.Show ( dlog ) > 0 ) then
local_mutate_threshold = dlog.lm_threshold.value
keep_bands = dlog.keep_bands.value
wiggle_with_filters_off = dlog.wwfo.value
test_for_stability = dlog.tfs.value
verbose = dlog.verbose.value
return true
else
return false
end
end
function GetParameters ()
local dlog = dialog.CreateDialog ( "Loop remix 11.0b" )
dlog.rb_length = dialog.AddSlider ( "Remix length" , remix_length , 3 , 8 , 0 )
dlog.min_residue = dialog.AddSlider ( "Min residue" , min_residue , 1 , n_residues , 0 )
dlog.max_residue = dialog.AddSlider ( "Max residue" , max_residue , 1 , n_residues , 0 )
dlog.remix_criterion = dialog.AddSlider ( "Remix criterion" , ss_criterion , 1 , 4 , 0 )
dlog.rc = dialog.AddLabel ( "1 = L only : 2 = L only + 1 : 3 = L + E : 4 = Any" )
dlog.n_remixes = dialog.AddSlider ( "N remixes" , number_of_remixes , 1 , 10 , 0 )
dlog.ok = dialog.AddButton ( "OK" , 1 )
dlog.cancel = dialog.AddButton ( "Cancel" , 0 )
dlog.other_options = dialog.AddButton ( "Other options" , 2 )
return_code = dialog.Show ( dlog )
if ( return_code > 0 ) then
remix_length = dlog.rb_length.value
min_residue = dlog.min_residue.value
max_residue = dlog.max_residue.value
ss_criterion = dlog.remix_criterion.value
number_of_remixes = dlog.n_remixes.value
end
return return_code
end
function main()
n_residues = structure.GetCount ()
min_residue = 1
max_residue = n_residues
-- Trim off locked terminal sequences as a UI convenience
min_residue = 1
while ( ( structure.IsLocked ( min_residue ) == true ) and ( min_residue <= n_residues ) ) do
min_residue = min_residue + 1
end
max_residue = n_residues
while ( ( structure.IsLocked ( max_residue ) == true ) and ( max_residue >= 1 ) ) do
max_residue = max_residue - 1
end
rb = 0
save.Quicksave ( kOriginalStructureOrNewBest )
behavior.SetClashImportance ( 1 )
repeat
dialog_code = GetParameters ()
if ( dialog_code == 2 ) then
GetOptionalParameters ()
end
until ( dialog_code < 2 )
if ( dialog_code == 0 ) then
return
end
print ( "Loop remix 10.0a" )
best_score = GetScore ()
max_residue = math.min ( n_residues , max_residue )
min_residue = math.max ( 1 , min_residue )
if ( max_residue < min_residue + remix_length - 1 ) then
print ( "Remix range error." )
print ( "Max residue (" .. max_residue .. ") must be greater than or equal to Min residue (" .. min_residue .. ")" )
print ( "plus Remix Length (" .. remix_length .. ")" )
return
end
for i = 1, n_residues do
original_secondary_structure [ i ] = structure.GetSecondaryStructure ( i )
end
PrintParameters ()
start_time = os.time ()
if ( keep_bands == false ) then
band.DisableAll ()
end
if ( wiggle_with_filters_off == true ) then
GetFilterList ()
end
GetListOfFixedResidues ()
for i = 1 , n_residues do
original_residues [ i ] = structure.GetAminoAcid ( i )
end
while true do
RemixAll ()
end
save.Quickload ( kOriginalStructureOrNewBest )
if ( keep_bands == false ) then
band.EnableAll ()
end
end
function cleanup ()
print ( "Cleaning up" )
behavior.SetClashImportance ( 1.0 )
save.Quickload ( kOriginalStructureOrNewBest )
if ( wiggle_with_filters_off == true ) then
TurnFiltersOn ()
end
selection.SelectAll ()
if ( keep_bands == false ) then
band.EnableAll ()
end
end_time = os.time ()
print ( "Elapsed time : " .. os.difftime ( end_time , start_time ) .. " secs" )
if ( #loss_from_low_ci_wiggle > 1 ) then
table.sort ( loss_from_low_ci_wiggle )
mid_pt = math.ceil ( #loss_from_low_ci_wiggle / 2 )
print ( "Median loss from low ci (" .. kLowCiWiggle .. ") wiggle " .. r3 ( loss_from_low_ci_wiggle [ mid_pt ] ) )
end
for i = 1 , n_residues do
new_residues [ i ] = structure.GetAminoAcid ( i )
end
for i = 1 , n_residues do
if ( new_residues [ i ] ~= original_residues [ i ] ) then
print ( i .. " : " .. aa1_to_aa3 ( original_residues [ i ] ) .. " -> " .. aa1_to_aa3 ( new_residues [ i ] ) )
end
end
end
--main ()
xpcall ( main , cleanup )