Skip to content

Commit 7a4b585

Browse files
committed
Added possibility to disable penalty between two different families
1 parent 4b518f0 commit 7a4b585

File tree

6 files changed

+40
-25
lines changed

6 files changed

+40
-25
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,14 +258,15 @@ An example, illustrated with Chimera software, simulated trajectory of structure
258258
| LEF_DRIFT | True if LEFs are pushed back when they encounter other LEFs. | bool | False |
259259
| N_LEF | Number of loop extrusion factors. | int | None |
260260
| N_LEF2 | Number of second family loop extrusion factors. | int | 0 |
261+
| CROSS_LOOP | True if the penalty is applied when mi<mj<ni<nj. False if it applies only when mj=ni. When false it is better to enable LEF_DRIFT as well. | bool |True |
262+
| BETWEEN_FAMILIES_PENALTY | Penalty applied when loops from different LEF families cross. | bool | True |
261263

262264
#### Energy Coefficients
263265
| Argument Name | Description | Type | Default Value |
264266
|------------------------|-----------------------------------------------------------------------------------------------------------------|------------|---------------------|
265267
| FOLDING_COEFF | Folding coefficient. | float | 1.0 |
266268
| FOLDING_COEFF2 | Folding coefficient for the second family of LEFs. | float | 0.0 |
267269
| CROSS_COEFF | LEF crossing coefficient. | float | 1.0 |
268-
| CROSS_LOOP | True if the penalty is applied when mi<mj<ni<nj. False if it applies only when mj=ni. When false it is better to enable LEF_DRIFT as well. | bool |True |
269270
| BIND_COEFF | CTCF binding coefficient. | float | 1.0 |
270271
| BW_STRENGTHS | List of strengths of the energy (floats) corresponding to each BW file. This equivalent to the `r` parameter in the LoopSage paper. | list | None |
271272

config.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ BURNIN = 1000
1818
T_INIT = 2.0
1919
T_FINAL = 0.01
2020
METHOD = Metropolis
21+
BETWEEN_FAMILIES_PENALTY = False
2122

2223
; Molecular Dynamics
2324
INITIAL_STRUCTURE_TYPE = rw

config_auto.ini

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
####################
44

55
# This is automatically generated config file.
6-
# Generated at: 2025-04-17T12:52:24.311211
6+
# Generated at: 2025-04-22T13:12:24.198124
77

88
# Notes:
99
# Some fields require units. Units are represented as objects from mm.units module.
@@ -96,6 +96,9 @@ BW_STRENGTHS = []
9696
; It true if the penalty is applied for situations mi<mj<ni<nj and mi=nj, and false if it is applied only for mi=nj., type: bool, default: True
9797
CROSS_LOOP = True
9898

99+
; Penalty for LEF2s that are crossing LEFs., type: bool, default: True
100+
BETWEEN_FAMILIES_PENALTY = False
101+
99102
; CTCF binding coefficient., type: float, default: 1.0
100103
BIND_COEFF = 1.0
101104

loopsage/args_definition.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ def write_config_file(self):
192192
Arg('CROSS_COEFF', help="LEF crossing coefficient.", type=float, default='1.0', val='1.0'),
193193
Arg('BW_STRENGTHS', help="List of strengths of the energy (floats) corresponding to each BW file. This equivalent to the `r` parameter in the LoopSage paper.", type=list, nargs='+', default=[], val=[]),
194194
Arg('CROSS_LOOP', help="It true if the penalty is applied for situations mi<mj<ni<nj and mi=nj, and false if it is applied only for mi=nj.", type=bool, default='True', val='True'),
195+
Arg('BETWEEN_FAMILIES_PENALTY', help="Penalty for LEF2s that are crossing LEFs.", type=bool, default='True', val='True'),
195196
Arg('BIND_COEFF', help="CTCF binding coefficient.", type=float, default='1.0', val='1.0'),
196197
Arg('SAVE_PLOTS', help="It should be true in case that you would like to save diagnostic plots. In case that you use small MC_STEP or large N_STEPS is better to mark it as False.", type=bool, default='True', val='True'),
197198
Arg('SAVE_MDT', help="In case that you would liketo save metadata of the stochastic simulation.", type=bool, default='True', val='True'),

loopsage/run.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def main():
7474
# Simulation Strengths
7575
f, f2, b, kappa = args.FOLDING_COEFF, args.FOLDING_COEFF2, args.BIND_COEFF, args.CROSS_COEFF
7676
r = args.BW_STRENGTHS
77+
between_families_penalty = args.BETWEEN_FAMILIES_PENALTY # Added argument
7778

7879
# Definition of region
7980
region, chrom = [args.REGION_START,args.REGION_END], args.CHROM
@@ -84,7 +85,7 @@ def main():
8485

8586
# Run Simulation
8687
sim = StochasticSimulation(region,chrom,bedpe_file,out_dir=output_name,N_beads=N_beads,N_lef=N_lef,N_lef2=N_lef2, bw_files=bw_paths, track_file=args.LEF_TRACK_FILE)
87-
Es, Ms, Ns, Bs, Ks, Fs, ufs = sim.run_energy_minimization(N_steps,MC_step,burnin,T,T_min,mode=mode,viz=args.SAVE_PLOTS,save=args.SAVE_MDT,lef_rw=args.LEF_RW,f=f,f2=f2,b=b,kappa=kappa,lef_drift=args.LEF_DRIFT,cross_loop=args.CROSS_LOOP,r=r)
88+
Es, Ms, Ns, Bs, Ks, Fs, ufs = sim.run_energy_minimization(N_steps,MC_step,burnin,T,T_min,mode=mode,viz=args.SAVE_PLOTS,save=args.SAVE_MDT,lef_rw=args.LEF_RW,f=f,f2=f2,b=b,kappa=kappa,lef_drift=args.LEF_DRIFT,cross_loop=args.CROSS_LOOP,r=r,between_families_penalty=between_families_penalty)
8889
if args.SIMULATION_TYPE=='EM':
8990
sim.run_EM(args.PLATFORM,args.ANGLE_FF_STRENGTH,args.LE_FF_LENGTH,args.LE_FF_STRENGTH,args.EV_FF_STRENGTH,args.EV_FF_POWER,args.TOLERANCE,args.FRICTION,args.INTEGRATOR_STEP,args.SIM_TEMP,args.INITIAL_STRUCTURE_TYPE,args.VIZ_HEATS,args.FORCEFIELD_PATH)
9091
elif args.SIMULATION_TYPE=='MD':

loopsage/stochastic_simulation.py

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,15 @@ def E_bind(L, R, ms, ns, bind_norm):
4747
return E_b
4848

4949
@njit
50-
def E_cross(ms, ns, k_norm, cross_loop):
50+
def E_cross(ms, ns, k_norm, N_lef, cross_loop=True, between_families_penalty=True):
5151
'''
5252
The crossing energy.
5353
'''
5454
crossing = 0.0
5555
for i in prange(len(ms)):
5656
for j in range(i + 1, len(ms)):
57-
crossing += Kappa(ms[i], ns[i], ms[j], ns[j], cross_loop)
57+
if between_families_penalty or (i < N_lef and j < N_lef) or (i >= N_lef and j >= N_lef):
58+
crossing += Kappa(ms[i], ns[i], ms[j], ns[j], cross_loop)
5859
return k_norm * crossing
5960

6061
@njit
@@ -76,11 +77,11 @@ def E_bw(N_bws, r, BWs, ms, ns):
7677
return E_bw
7778

7879
@njit
79-
def get_E(L, R, bind_norm, fold_norm, fold_norm2, k_norm, ms, ns, N_lef, N_lef2, cross_loop, r=None, N_bws=0, BWs=None):
80+
def get_E(L, R, bind_norm, fold_norm, fold_norm2, k_norm, ms, ns, N_lef, N_lef2, cross_loop, r=None, N_bws=0, BWs=None, between_families_penalty=True):
8081
''''
8182
The total energy.
8283
'''
83-
energy = E_bind(L, R, ms, ns, bind_norm) + E_cross(ms, ns, k_norm, cross_loop) + E_fold(ms, ns, fold_norm)
84+
energy = E_bind(L, R, ms, ns, bind_norm) + E_cross(ms, ns, k_norm, cross_loop, between_families_penalty) + E_fold(ms, ns, fold_norm)
8485
if fold_norm2!=0: energy += E_fold(ms[N_lef:N_lef+N_lef2],ns[N_lef:N_lef+N_lef2], fold_norm2)
8586
if r is not None and BWs is not None: energy += E_bw(N_bws, r, BWs, ms, ns)
8687
return energy
@@ -107,19 +108,20 @@ def get_dE_bw(N_bws, r, BWs, ms, ns, m_new, n_new, idx):
107108
return dE_bw
108109

109110
@njit
110-
def get_dE_cross(ms, ns, m_new, n_new, idx, k_norm, cross_loop):
111+
def get_dE_cross(ms, ns, m_new, n_new, idx, k_norm, cross_loop, N_lef, between_families_penalty):
111112
'''
112113
Energy difference for crossing energy.
113114
'''
114115
K1, K2 = 0, 0
115116
for i in prange(len(ms)):
116117
if i != idx:
117-
K1 += Kappa(ms[idx], ns[idx], ms[i], ns[i], cross_loop)
118-
K2 += Kappa(m_new, n_new, ms[i], ns[i], cross_loop)
118+
if between_families_penalty or (idx < N_lef and i < N_lef) or (idx >= N_lef and i >= N_lef):
119+
K1 += Kappa(ms[idx], ns[idx], ms[i], ns[i], cross_loop)
120+
K2 += Kappa(m_new, n_new, ms[i], ns[i], cross_loop)
119121
return k_norm * (K2 - K1)
120122

121123
@njit
122-
def get_dE(L, R, bind_norm, fold_norm, fold_norm2, k_norm, ms, ns, m_new, n_new, idx, N_lef, N_lef2, cross_loop, r=None, N_bws=0, BWs=None):
124+
def get_dE(L, R, bind_norm, fold_norm, fold_norm2, k_norm, ms, ns, m_new, n_new, idx, N_lef, N_lef2, cross_loop, r=None, N_bws=0, BWs=None, between_families_penalty=True):
123125
'''
124126
Total energy difference.
125127
'''
@@ -129,7 +131,7 @@ def get_dE(L, R, bind_norm, fold_norm, fold_norm2, k_norm, ms, ns, m_new, n_new,
129131
else:
130132
dE += get_dE_fold(fold_norm2,ms[N_lef:N_lef+N_lef2],ns[N_lef:N_lef+N_lef2],m_new,n_new,idx-N_lef)
131133
dE += get_dE_bind(L, R, bind_norm, ms, ns, m_new, n_new, idx)
132-
dE += get_dE_cross(ms, ns, m_new, n_new, idx, k_norm, cross_loop)
134+
dE += get_dE_cross(ms, ns, m_new, n_new, idx, k_norm, cross_loop, N_lef, between_families_penalty)
133135
if r is not None and BWs is not None: dE += get_dE_bw(N_bws, r, BWs, ms, ns, m_new, n_new, idx)
134136
return dE
135137

@@ -185,14 +187,14 @@ def initialize(N_beads, N_lef, track=None):
185187
return ms, ns
186188

187189
@njit
188-
def run_simulation(N_beads, N_steps, MC_step, burnin, T, T_min, fold_norm, fold_norm2, bind_norm, k_norm, N_lef, N_lef2, L, R, mode, lef_rw=True, lef_drift=True, cross_loop=True, r=None, N_bws=0, BWs=None, track=None):
190+
def run_simulation(N_beads, N_steps, MC_step, burnin, T, T_min, fold_norm, fold_norm2, bind_norm, k_norm, N_lef, N_lef2, L, R, mode, lef_rw=True, lef_drift=True, cross_loop=True, r=None, N_bws=0, BWs=None, track=None, between_families_penalty=True):
189191
'''
190192
Runs the Monte Carlo simulation.
191193
'''
192194
Ti = T
193195
bi = burnin // MC_step
194196
ms, ns = initialize(N_beads, N_lef + N_lef2, track)
195-
E = get_E(L, R, bind_norm, fold_norm, fold_norm2, k_norm, ms, ns, N_lef, N_lef2, cross_loop, r, N_bws, BWs)
197+
E = get_E(L, R, bind_norm, fold_norm, fold_norm2, k_norm, ms, ns, N_lef, N_lef2, cross_loop, r, N_bws, BWs, between_families_penalty)
196198
Es, Ks, Fs, Bs, ufs = np.zeros(N_steps // MC_step, dtype=np.float64), np.zeros(N_steps // MC_step, dtype=np.float64), np.zeros(N_steps // MC_step, dtype=np.float64), np.zeros(N_steps // MC_step, dtype=np.float64), np.zeros(N_steps // MC_step, dtype=np.float64)
197199
Ms, Ns = np.zeros((N_lef + N_lef2, N_steps // MC_step), dtype=np.int64), np.zeros((N_lef + N_lef2, N_steps // MC_step), dtype=np.int64)
198200

@@ -207,7 +209,7 @@ def run_simulation(N_beads, N_steps, MC_step, burnin, T, T_min, fold_norm, fold_
207209
m_new, n_new = slide(ms[j], ns[j], ms, ns, N_beads, lef_rw, lef_drift)
208210

209211
# Compute energy difference
210-
dE = get_dE(L, R, bind_norm, fold_norm, fold_norm2, k_norm, ms, ns, m_new, n_new, j, N_lef, N_lef2, cross_loop, r, N_bws, BWs)
212+
dE = get_dE(L, R, bind_norm, fold_norm, fold_norm2, k_norm, ms, ns, m_new, n_new, j, N_lef, N_lef2, cross_loop, r, N_bws, BWs, between_families_penalty)
211213

212214
if dE <= 0 or np.exp(-dE / Ti) > np.random.rand():
213215
ms[j], ns[j] = m_new, n_new
@@ -220,7 +222,7 @@ def run_simulation(N_beads, N_steps, MC_step, burnin, T, T_min, fold_norm, fold_
220222
if i % MC_step == 0:
221223
ufs[i // MC_step] = unfolding_metric(ms, ns, N_beads)
222224
Es[i // MC_step] = E
223-
Ks[i // MC_step] = E_cross(ms, ns, k_norm, cross_loop)
225+
Ks[i // MC_step] = E_cross(ms, ns, k_norm, cross_loop, N_lef, between_families_penalty)
224226
Fs[i // MC_step] = E_fold(ms, ns, fold_norm)
225227
Bs[i // MC_step] = E_bind(L, R, ms, ns, bind_norm)
226228
return Ms, Ns, Es, Ks, Fs, Bs, ufs
@@ -251,7 +253,7 @@ def __init__(self,region,chrom,bedpe_file,N_beads=None,N_lef=None,N_lef2=0,out_d
251253
print('Number of LEFs:',self.N_lef+self.N_lef2)
252254
self.path = make_folder(out_dir)
253255

254-
def run_energy_minimization(self, N_steps, MC_step, burnin, T=1, T_min=0, mode='Metropolis', viz=False, save=False, f=1.0, f2=0.0, b=1.0, kappa=1.0, lef_rw=True, lef_drift=True, cross_loop=True, r=None):
256+
def run_energy_minimization(self, N_steps, MC_step, burnin, T=1, T_min=0, mode='Metropolis', viz=False, save=False, f=1.0, f2=0.0, b=1.0, kappa=1.0, lef_rw=True, lef_drift=True, cross_loop=True, r=None, between_families_penalty=True):
255257
'''
256258
Implementation of the stochastic Monte Carlo simulation.
257259
@@ -265,6 +267,7 @@ def run_energy_minimization(self, N_steps, MC_step, burnin, T=1, T_min=0, mode='
265267
r (list): strength of each ChIP-Seq experiment.
266268
N_bws (int): number of binding weight matrices.
267269
BWs (np.ndarray): binding weight matrices.
270+
between_families_penalty (bool): whether to apply penalty for interactions between families.
268271
'''
269272
# Define normalization constants
270273
fold_norm, fold_norm2, bind_norm, k_norm = -self.N_beads*f/((self.N_lef+self.N_lef2)*np.log(self.N_beads/(self.N_lef+self.N_lef2))), -self.N_beads*f2/((self.N_lef+self.N_lef2)*np.log(self.N_beads/(self.N_lef+self.N_lef2))), -self.N_beads*b/(np.sum(self.L)+np.sum(self.R)), kappa*1e4
@@ -275,7 +278,7 @@ def run_energy_minimization(self, N_steps, MC_step, burnin, T=1, T_min=0, mode='
275278
print('\nRunning simulation (with parallelization across CPU cores)...')
276279
start = time.time()
277280
self.burnin = burnin
278-
self.Ms, self.Ns, self.Es, self.Ks, self.Fs, self.Bs, self.ufs = run_simulation(self.N_beads, N_steps, MC_step, burnin, T, T_min, fold_norm, fold_norm2, bind_norm, k_norm, self.N_lef, self.N_lef2, self.L, self.R, mode, lef_rw, lef_drift, cross_loop, r, self.N_bws, self.BWs, self.lef_track)
281+
self.Ms, self.Ns, self.Es, self.Ks, self.Fs, self.Bs, self.ufs = run_simulation(self.N_beads, N_steps, MC_step, burnin, T, T_min, fold_norm, fold_norm2, bind_norm, k_norm, self.N_lef, self.N_lef2, self.L, self.R, mode, lef_rw, lef_drift, cross_loop, r, self.N_bws, self.BWs, self.lef_track, between_families_penalty)
279282
end = time.time()
280283
elapsed = end - start
281284
print(f'Computation finished successfully in {elapsed//3600:.0f} hours, {elapsed%3600//60:.0f} minutes and {elapsed%60:.0f} seconds.')
@@ -294,7 +297,7 @@ def run_energy_minimization(self, N_steps, MC_step, burnin, T=1, T_min=0, mode='
294297
file.write(f'Folding energy in equilibrium is {np.average(self.Fs[burnin//MC_step:]):.2f}. Folding coefficient f={f}. Folding coefficient for the second family f2={f2}\n')
295298
file.write(f'Binding energy in equilibrium is {np.average(self.Bs[burnin//MC_step:]):.2f}. Binding coefficient b={b}.\n')
296299
if r is not None and self.BWs is not None:
297-
file.write(f'RNApII binding energy included with {N_bws} binding weight matrices.\n')
300+
file.write(f'RNApII binding energy included with {self.N_bws} binding weight matrices.\n')
298301
file.write(f'Energy at equilibrium: {np.average(self.Es[self.burnin//MC_step:]):.2f}.\n')
299302
np.save(save_dir + 'Ms.npy', self.Ms)
300303
np.save(save_dir + 'Ns.npy', self.Ns)
@@ -342,22 +345,27 @@ def main():
342345
# Definition of Monte Carlo parameters
343346
N_steps, MC_step, burnin, T, T_min = int(4e4), int(5e2), 1000, 3.0, 1.0
344347
N_lef, N_lef2 = 100, 20
345-
lew_rw=True
348+
lew_rw = True
346349
mode = 'Annealing'
347350

348351
# Simulation Strengths
349352
f, f2, b, kappa = 1.0, 2.0, 1.0, 1.0
350353

351354
# Definition of region
352-
region, chrom = [15550000,16850000], 'chr6'
355+
region, chrom = [15550000, 16850000], 'chr6'
353356

354357
# Definition of data
355-
output_name='tmp'
358+
output_name = 'tmp'
356359
bedpe_file = '/home/skorsak/Data/HiChIP/Maps/hg00731_smc1_maps_2.bedpe'
357360

358-
sim = StochasticSimulation(region,chrom,bedpe_file,out_dir=output_name,N_beads=1000,N_lef=N_lef,N_lef2=N_lef2)
359-
Es, Ms, Ns, Bs, Ks, Fs, ufs = sim.run_energy_minimization(N_steps,MC_step,burnin,T,T_min,mode=mode,viz=True,save=True,f=f,f2=f2, b=b, kappa=kappa, lef_rw=lew_rw)
360-
sim.run_MD('CUDA',continuous_topoisomerase=True, p_ev=0.01)
361+
# Between family penalty
362+
between_families_penalty = True
363+
364+
sim = StochasticSimulation(region, chrom, bedpe_file, out_dir=output_name, N_beads=1000, N_lef=N_lef, N_lef2=N_lef2)
365+
Es, Ms, Ns, Bs, Ks, Fs, ufs = sim.run_energy_minimization(
366+
N_steps, MC_step, burnin, T, T_min, mode=mode, viz=True, save=True, f=f, f2=f2, b=b, kappa=kappa, lef_rw=lew_rw, between_families_penalty=between_families_penalty
367+
)
368+
sim.run_MD('CUDA', continuous_topoisomerase=True, p_ev=0.01)
361369

362370
if __name__=='__main__':
363371
main()

0 commit comments

Comments
 (0)