1 |
package proteinstructure; |
2 |
import java.util.HashMap; |
3 |
import java.util.TreeMap; |
4 |
import java.util.ArrayList; |
5 |
import java.lang.Math; |
6 |
import java.util.Collections; |
7 |
|
8 |
public class ContactMap { |
9 |
|
10 |
// NOTE: residueNums and residueTypes contain also the unobserved or non standard residues |
11 |
// residues and nums2serials contain only observed standard amino acids |
12 |
// If someone wants to check if i_num or i is unobserved, |
13 |
// then this can be done by checking whether nums2serials has such key (for i_num) or value (for i) |
14 |
// WHERE DO WE USE i or i_num: |
15 |
// - i: for contact map iteration and sequence separation |
16 |
// - i_num: for residue types and common neighbours |
17 |
public Integer[] residueNums; // array with all residue nums |
18 |
public String[] residueTypes; // array with all 1-letter code residue type |
19 |
public TreeMap<Integer,String> residues = new TreeMap<Integer,String>();// map from nums to types |
20 |
public HashMap<Integer,Integer> nums2serials = new HashMap<Integer,Integer>(); // map from residue nums to residue serials (i.e. the indexes of the arrays above) |
21 |
|
22 |
public int t; // size of contact map |
23 |
public int effT; // effective size of contact map (do not count the SKIPPED cells) |
24 |
public int l; // length of contact map/protein |
25 |
public int numObsStandAA = 0; // Nr. of standard observed amino acids |
26 |
public int numContacts = 0; // Nr. of contacts |
27 |
|
28 |
public EdgeState[][] CM; |
29 |
public HashMap<Integer,HashMap<Integer,TreeMap<Integer,String>>> CNs; // hashmap containing neighbourhoods, access a single one through getComNbs |
30 |
|
31 |
public ContactMap() { |
32 |
} |
33 |
|
34 |
/** |
35 |
* Constructs a ContactMap passing a list of contacts, the full residue sequence and a map of residue nums to types (only observed standard aas) |
36 |
* @param contacts ArrayList of contacts (as Contact objects) |
37 |
* @param residues map from num to types (only observed standard aas) |
38 |
* @param sequence String with full sequence (i.e. also unobserved and non-standard residues, denoted by X) |
39 |
*/ |
40 |
public ContactMap(ArrayList<Contact> contacts, TreeMap<Integer,String> residues, String sequence) { |
41 |
this.residues=residues; |
42 |
if (sequence.equals("")){ // no sequence given: we use residues only to initialise arrays |
43 |
int i=0; |
44 |
int maxnum = Collections.max(residues.keySet()); |
45 |
residueTypes = new String[maxnum]; |
46 |
residueNums = new Integer[maxnum]; |
47 |
// if residues TreeMap has correct residue numbering then this should take care of gaps, except for gaps at the end of the sequence |
48 |
for (int num=1;num<=maxnum;num++) { |
49 |
// residueNums will contain all nums starting from 1 to the maxnum |
50 |
residueNums[i]=num; |
51 |
if (residues.containsKey(num)) { |
52 |
// we put the observed nums into residueTypes |
53 |
residueTypes[i]=residues.get(num); |
54 |
// in nums2serials we want only observed residues thus we only add nums that are in residues TreeMap |
55 |
nums2serials.put(num, i); |
56 |
} else { |
57 |
// and for the unobserved nums we use an "X" for the residue type |
58 |
residueTypes[i]="X"; |
59 |
} |
60 |
i++; |
61 |
} |
62 |
} else { // if sequence given (full sequence): we use residues+sequence to initialise arrays |
63 |
// we create residueTypes and residueNums arrays from full sequence string |
64 |
residueTypes = new String[sequence.length()]; |
65 |
residueNums = new Integer[sequence.length()]; |
66 |
for (int i=0;i<sequence.length();i++){ |
67 |
residueTypes[i] = String.valueOf(sequence.charAt(i)); |
68 |
residueNums[i] = i+1; |
69 |
} |
70 |
// we initialise nums2serials using residues and residueNums |
71 |
nums2serials = new HashMap<Integer,Integer>(); |
72 |
for (int i=0;i<residueNums.length;i++){ |
73 |
// nums2serials must only contain the standard observed aa, so only members of residues TreeMap |
74 |
if (residues.containsKey(residueNums[i])){ |
75 |
nums2serials.put(residueNums[i],i); |
76 |
} |
77 |
} |
78 |
} |
79 |
// initialisation of l, numObsStandAA, t, effT from 4 above |
80 |
l = this.residueNums.length; |
81 |
numObsStandAA = this.residues.size(); |
82 |
t = (int)((l*(l-1))/2); |
83 |
effT = (int)((numObsStandAA*(numObsStandAA-1))/2); |
84 |
// initialisation of CM |
85 |
CM = new EdgeState[l][l]; |
86 |
// first we fill with NONCONTACTs (or SKIPPEDs if the i,j is not in nums2serials) |
87 |
for (int i=0;i<l;i++){ |
88 |
for (int j=0;j<l;j++) { |
89 |
if ((!this.nums2serials.containsValue(i)) || (!this.nums2serials.containsValue(j))) { |
90 |
CM[i][j] = EdgeState.SKIPPED; |
91 |
} else { |
92 |
CM[i][j] = EdgeState.NONCONTACT; |
93 |
} |
94 |
} |
95 |
} |
96 |
// then we fill the CONTACTs |
97 |
// the contacts ArrayList we pass as argument must contain the full bidirectional list of contacts |
98 |
for (Contact cont:contacts){ |
99 |
int i_num = cont.i; |
100 |
int j_num = cont.j; |
101 |
CM[nums2serials.get(i_num)][nums2serials.get(j_num)]=EdgeState.CONTACT; |
102 |
} |
103 |
// and initialise numContacts |
104 |
numContacts = contacts.size(); |
105 |
// finally we initialise the CNs HashMap |
106 |
getAllComNbs(); |
107 |
} |
108 |
|
109 |
/** |
110 |
* Gets the number of contacts, non contacts and skipped cells due to unobserved residues |
111 |
* from the part of the ContactMap >=(above) the given diagonal |
112 |
* @param diagonal |
113 |
* @return |
114 |
*/ |
115 |
public int[] getCMStats(int diagonal) { |
116 |
int[] result = new int[3]; |
117 |
for (int d=Math.max(1, diagonal);d<l;d++){ |
118 |
for (int i=0, j=i+d;i<l-d+1 && j<l;i++, j++){ |
119 |
if (CM[i][j] == EdgeState.CONTACT) result[0]++; |
120 |
if (CM[i][j] == EdgeState.NONCONTACT) result[1]++; |
121 |
if (CM[i][j] == EdgeState.SKIPPED) result[2]++; |
122 |
} |
123 |
} |
124 |
return result; |
125 |
} |
126 |
|
127 |
/** |
128 |
* Gets the common neighbourhood for residues i_num, j_num from the precomputed HashMap. |
129 |
* If there is no neighborhood between the two residues it returns an empty TreeMap |
130 |
* @param i_num |
131 |
* @param j_num |
132 |
* @return |
133 |
*/ |
134 |
public TreeMap<Integer,String> getComNbs(int i_num, int j_num) { |
135 |
// we initialise to an empty TreeMap, if there is no cn for i_num, j_num we return it empty |
136 |
TreeMap<Integer,String> cn = new TreeMap<Integer,String>(); |
137 |
|
138 |
if (CNs.containsKey(i_num) && CNs.get(i_num).containsKey(j_num)) { |
139 |
cn = CNs.get(i_num).get(j_num); |
140 |
} |
141 |
|
142 |
return cn; |
143 |
} |
144 |
|
145 |
/** |
146 |
* Gets the common neighbourhood for residues i_num, j_num from the precomputed HashMap |
147 |
* that are >= (above)/<= (below) the given diagonal |
148 |
* If there is no neighborhood between the two residues it returns an empty TreeMap |
149 |
* @param i_num |
150 |
* @param j_num |
151 |
* @param diagonal |
152 |
* @param above |
153 |
* @return |
154 |
*/ |
155 |
public TreeMap<Integer,String> getComNbs(int i_num, int j_num, int diagonal, boolean above) { |
156 |
// we initialise to an empty TreeMap, if there is no cn for i_num, j_num we return it empty |
157 |
TreeMap<Integer,String> cn = new TreeMap<Integer,String>(); |
158 |
|
159 |
int threshold = diagonal; |
160 |
|
161 |
if (CNs.containsKey(i_num) && CNs.get(i_num).containsKey(j_num)) { |
162 |
cn = CNs.get(i_num).get(j_num); |
163 |
} |
164 |
|
165 |
if (!cn.isEmpty()) { |
166 |
ArrayList<Integer> nums2eliminate = new ArrayList<Integer>(); |
167 |
for (int num:cn.keySet()){ |
168 |
// here we eliminate all the common neighbours that are below or above the given diagonal |
169 |
// (first storing the keys in an ArrayList and then removing from TreeMap) |
170 |
// num, i_num and j_num are nums (i.e. serial numbers from db) |
171 |
if (above) { |
172 |
// just for clarification, the opposite condition is: Math.abs(n-i)>=j-i && Math.abs(n-j)>=j-i |
173 |
if ((Math.abs(nums2serials.get(num)-nums2serials.get(i_num)) < threshold || (Math.abs(nums2serials.get(num)-nums2serials.get(j_num)) < threshold))) { |
174 |
nums2eliminate.add(num); |
175 |
} |
176 |
} else { |
177 |
// just for clarification, the opposite condition is: Math.abs(n-i)<=j-i && Math.abs(n-j)<=j-i |
178 |
if ((Math.abs(nums2serials.get(num)-nums2serials.get(i_num)) > threshold || (Math.abs(nums2serials.get(num)-nums2serials.get(j_num)) > threshold))) { |
179 |
nums2eliminate.add(num); |
180 |
} |
181 |
} |
182 |
} |
183 |
for (int num:nums2eliminate){ |
184 |
cn.remove(num); //now we remove all the detected non-valid common neighbours |
185 |
} |
186 |
} |
187 |
|
188 |
return cn; |
189 |
} |
190 |
|
191 |
/** |
192 |
* Gets the common neighbourhood for residues i_num, j_num from the precomputed HashMap |
193 |
* that are >= (above)/<= (below) the current diagonal |
194 |
* If there is no neighborhood between the two residues it returns an empty TreeMap |
195 |
* @param i_num |
196 |
* @param j_num |
197 |
* @param above (true if above diagonal) |
198 |
* @return |
199 |
*/ |
200 |
public TreeMap<Integer,String> getComNbs(int i_num, int j_num, boolean above) { |
201 |
return getComNbs(i_num, j_num, Math.abs(nums2serials.get(j_num)-nums2serials.get(i_num)), above); |
202 |
} |
203 |
|
204 |
/** |
205 |
* Get all common neighbors for this contact map object. Updates the CNs member and also returns it |
206 |
* @return CNs |
207 |
*/ |
208 |
public HashMap<Integer,HashMap<Integer,TreeMap<Integer,String>>> getAllComNbs() { |
209 |
// first we reset the existing CNs |
210 |
this.CNs = new HashMap<Integer,HashMap<Integer,TreeMap<Integer,String>>>(); |
211 |
// initialise a cns4j HashMap which will store all cn TreeMaps for each j |
212 |
HashMap<Integer,TreeMap<Integer,String>> cns4j = new HashMap<Integer,TreeMap<Integer,String>>(); |
213 |
// initialise a cn TreeMap which will store all cns for a given i,j |
214 |
TreeMap<Integer,String> cn = new TreeMap<Integer,String>(); |
215 |
for (int i=0;i<l;i++){ |
216 |
for (int j=i+1;j<l;j++) { |
217 |
for (int k=0;k<l;k++) { // for each i,j we scan all possible contacts k |
218 |
if ((k != i) && (k != j) && |
219 |
CM[Math.min(i,k)][Math.max(i,k)].contact() && CM[Math.min(j,k)][Math.max(j,k)].contact()) { |
220 |
cn.put(residueNums[k],residueTypes[k]); |
221 |
// we could have used the following, but would be slower |
222 |
//updateCNsHashMap(residueNums[i],residueNums[j],residueNums[k]); |
223 |
} |
224 |
} // end k |
225 |
if (!cn.isEmpty()) { |
226 |
cns4j.put(residueNums[j], cn); |
227 |
} |
228 |
cn = new TreeMap<Integer,String>(); // reset cn for next k |
229 |
} // end j |
230 |
CNs.put(residueNums[i], cns4j); |
231 |
cns4j = new HashMap<Integer,TreeMap<Integer,String>>(); // reset cns4j for next i |
232 |
} // end i |
233 |
|
234 |
return this.CNs; |
235 |
} |
236 |
|
237 |
/** |
238 |
* Convenience method to update the monster CNs HashMap without thinking too much |
239 |
* @param i_num |
240 |
* @param j_num |
241 |
* @param k_num |
242 |
*/ |
243 |
public void updateCNsHashMap(int i_num, int j_num, int k_num) { |
244 |
if (CNs.containsKey(i_num)){ |
245 |
HashMap<Integer,TreeMap<Integer,String>> cns4j = CNs.get(i_num); |
246 |
if (cns4j.containsKey(j_num)){ |
247 |
cns4j.get(j_num).put(k_num, residueTypes[nums2serials.get(k_num)]); |
248 |
} else { |
249 |
TreeMap<Integer,String> cn = new TreeMap<Integer,String>(); |
250 |
cn.put(k_num, residueTypes[nums2serials.get(k_num)]); |
251 |
cns4j.put(j_num,cn); |
252 |
} |
253 |
} else { |
254 |
HashMap<Integer,TreeMap<Integer,String>> cns4j = new HashMap<Integer,TreeMap<Integer,String>>(); |
255 |
TreeMap<Integer,String> cn = new TreeMap<Integer,String>(); |
256 |
cn.put(k_num, residueTypes[nums2serials.get(k_num)]); |
257 |
cns4j.put(j_num, cn); |
258 |
CNs.put(i_num, cns4j); |
259 |
} |
260 |
} |
261 |
|
262 |
/** |
263 |
* Update all common neighbors given contact (this method should be used only for prediction) |
264 |
* @param i |
265 |
* @param j |
266 |
* @param |
267 |
* @return |
268 |
*/ |
269 |
public void updateComNbsGivenContact(int i, int j, boolean below) { |
270 |
|
271 |
if (CM[i][j].contact()) { // if contact predicted just in case |
272 |
|
273 |
if (below) { // if known contacts are only below the diagonal |
274 |
int d = Math.abs(i-j); |
275 |
int[] idxs = {i, j}; |
276 |
for (int idx:idxs){ |
277 |
for (int r=Math.max(0,idx-d);r<idx;r++) { |
278 |
if ((r != i) && CM[r][idx].contact()) { |
279 |
updateCNsHashMap(residueNums[Math.min(i,r)],residueNums[Math.max(i,r)],residueNums[idx]); |
280 |
} |
281 |
} |
282 |
|
283 |
for (int c=idx+1;c<=Math.min(l-1,idx+d);c++) { |
284 |
if ((c != j) && CM[idx][c].contact()) { |
285 |
updateCNsHashMap(residueNums[i],residueNums[c],residueNums[idx]); |
286 |
} |
287 |
} |
288 |
} |
289 |
|
290 |
for (int b=i+1; b<j; b++) { // update i-j's common neighbors |
291 |
if (CM[i][b].contact() && CM[b][j].contact()) { |
292 |
updateCNsHashMap(residueNums[i],residueNums[j],residueNums[b]); |
293 |
} |
294 |
} |
295 |
} else { // if known contacts can be also above the diagonal |
296 |
for (int k=0;k<l;k++) { |
297 |
if ((k != i) && (k != j)) { |
298 |
if (CM[i][j].contact() && CM[Math.min(i,k)][Math.max(i,k)].contact()) { |
299 |
updateCNsHashMap(residueNums[Math.min(j,k)],residueNums[Math.max(j,k)],residueNums[i]); |
300 |
} |
301 |
if (CM[i][j].contact() && CM[Math.min(j,k)][Math.max(j,k)].contact()) { |
302 |
updateCNsHashMap(residueNums[Math.min(i,k)],residueNums[Math.max(i,k)],residueNums[j]); |
303 |
} |
304 |
if (CM[Math.min(i,k)][Math.max(i,k)].contact() && CM[Math.min(j,k)][Math.max(j,k)].contact()) { |
305 |
updateCNsHashMap(residueNums[i],residueNums[j],residueNums[k]); |
306 |
} |
307 |
} |
308 |
} |
309 |
} |
310 |
} |
311 |
} |
312 |
|
313 |
/** |
314 |
* Returns true if this contact map has no contacts at all |
315 |
* @return |
316 |
*/ |
317 |
public boolean hasNoContacts() { |
318 |
boolean noContacts = true; |
319 |
for (int i=0;i<l;i++){ |
320 |
for (int j=i+1;j<l;j++){ |
321 |
if (this.CM[i][j].contact()) { |
322 |
noContacts = false; |
323 |
return noContacts; |
324 |
} |
325 |
} |
326 |
} |
327 |
return noContacts; |
328 |
} |
329 |
|
330 |
/** |
331 |
* Returns whether the given cell has at least N common neighbors |
332 |
* @param i_num |
333 |
* @param j_num |
334 |
* @param N |
335 |
* @return |
336 |
*/ |
337 |
public boolean cellHasNComNbs(int i_num, int j_num, int N){ |
338 |
TreeMap<Integer,String> cn = getComNbs(i_num, j_num); |
339 |
boolean hasNCNs = false; |
340 |
if (((N==0) && cn.isEmpty()) || ((N!=0) && cn.size()>=N)) hasNCNs=true; |
341 |
return hasNCNs; |
342 |
} |
343 |
|
344 |
/** |
345 |
* Returns number of contacts in the whole contact map with at least N common neighbors |
346 |
* Useful to find out how many contacts don't have common neighbors at all (N=0) |
347 |
* Will only count the ones above given diagonal value (use diagonal=1 for count in whole contact map) |
348 |
* @param N |
349 |
* @param diagonal |
350 |
* @return |
351 |
*/ |
352 |
public int getNumContactsWithNComNbs(int N, int diagonal){ |
353 |
int numContactsWithNcns = 0; |
354 |
for (int d=Math.max(1, diagonal);d<l;d++){ |
355 |
for (int i=0, j=i+d;i<l-d+1 && j<l;i++, j++){ |
356 |
if (CM[i][j].contact()){ |
357 |
if (cellHasNComNbs(residueNums[i], residueNums[j], N)){ |
358 |
numContactsWithNcns++; |
359 |
} |
360 |
} |
361 |
} |
362 |
} |
363 |
return numContactsWithNcns; |
364 |
} |
365 |
|
366 |
/** |
367 |
* Puts into a new ContactMap object the part of this ContactMap that is strictly below the given diagonal (i.e. not including the diagonal itself) |
368 |
* The rest is filled with NONCONTACT |
369 |
* @param diagonal |
370 |
* @return |
371 |
*/ |
372 |
//TODO we fill above range diagonals with NONCONTACT, thus we are "forcing" the return object to be a OrigCM but we want this method for ContactMap's, how to solve this? |
373 |
public ContactMap cutCMToBelowRange(int diagonal) { |
374 |
ContactMap newcm = this.semiDeepCopy(true); |
375 |
for (int d=1;d<Math.min(diagonal,l);d++){ |
376 |
for (int i=0, j=i+d;i<l-d+1 && j<l;i++, j++){ |
377 |
newcm.CM[i][j]=this.CM[i][j]; |
378 |
} |
379 |
} |
380 |
|
381 |
for (int d=Math.min(diagonal,l);d<l;d++) { |
382 |
for (int i=0, j=i+d;i<l-d+1 && j<l;i++, j++){ |
383 |
newcm.CM[i][j] = EdgeState.NONCONTACT; |
384 |
} |
385 |
} |
386 |
newcm.getAllComNbs(); // finally we re-calculate the common neighbours for the new contact map |
387 |
newcm.updateNumContacts(); // and we recount contacts |
388 |
return newcm; |
389 |
} |
390 |
|
391 |
/** |
392 |
* From all contacts in smallerCM, gets a contact map which contains only those contacts that are reachable from this ContactMap |
393 |
* @param smallerCM |
394 |
* @return |
395 |
*/ |
396 |
public ContactMap getReachable(ContactMap smallerCM){ |
397 |
ContactMap newcm = this.semiDeepCopy(true); |
398 |
for (int i=0;i<l;i++) { |
399 |
for (int j=i+1;j<l;j++){ |
400 |
// we loop through all contacts of this contact map |
401 |
if (this.CM[i][j].contact()) { |
402 |
// if for this contact there's at least 1 cn in smallerCM |
403 |
if (smallerCM.cellHasNComNbs(residueNums[i],residueNums[j], 1)){ |
404 |
// we assign contact, the rest is kept as it was initialised with semiDeepCopy |
405 |
newcm.CM[i][j]=EdgeState.CONTACT; |
406 |
} |
407 |
} |
408 |
} |
409 |
} |
410 |
newcm.getAllComNbs(); // finally we re-calculate the CNs for the new ContactMap before returning it |
411 |
newcm.updateNumContacts(); // and we recount contacts |
412 |
return newcm; |
413 |
} |
414 |
|
415 |
/** |
416 |
* Returns a new ContactMap result of subtracting smallerCM ContactMap from this ContactMap (set subtraction) |
417 |
* @param smallerCM |
418 |
* @return |
419 |
*/ |
420 |
public ContactMap subtract (ContactMap smallerCM){ |
421 |
ContactMap newcm = this.semiDeepCopy(false); // we copied this cm with all its contacts |
422 |
for (int i=0;i<l;i++) { |
423 |
for (int j=i+1;j<l;j++){ |
424 |
// if contact exists in bigger set and also in smaller set then we eliminate it |
425 |
if (this.CM[i][j].contact() && smallerCM.CM[i][j].contact()) { |
426 |
// we assign NONCONTACT |
427 |
newcm.CM[i][j]=EdgeState.NONCONTACT; |
428 |
} |
429 |
} |
430 |
} |
431 |
newcm.getAllComNbs(); // finally we re-calculate the CNs for the new ContactMap before returning it |
432 |
newcm.updateNumContacts(); // and we recount contacts |
433 |
return newcm; |
434 |
} |
435 |
|
436 |
/** |
437 |
* Returns a new ContactMap result of adding secondCM to this ContactMap (set union) |
438 |
* @param secondCM |
439 |
* @return |
440 |
*/ |
441 |
public ContactMap add (ContactMap secondCM){ |
442 |
ContactMap newcm = this.semiDeepCopy(false); // we copied this cm with all its contacts |
443 |
for (int i=0;i<l;i++) { |
444 |
for (int j=i+1;j<l;j++){ |
445 |
// if contact doesn't exist in first set, but exists in second set then we add the contact to the resulting set |
446 |
if (!this.CM[i][j].contact() && secondCM.CM[i][j].contact()) { |
447 |
// we assign CONTACT |
448 |
newcm.CM[i][j]=EdgeState.CONTACT; |
449 |
} |
450 |
} |
451 |
} |
452 |
newcm.getAllComNbs(); // finally we re-calculate the CNs for the new ContactMap before returning it |
453 |
newcm.updateNumContacts(); // and we recount contacts |
454 |
return newcm; |
455 |
} |
456 |
|
457 |
/** |
458 |
* The method performs a semi deep copy of a ContactMap. By semi deep I mean: |
459 |
* primitives are copied, CM and CNs are copied but residueNums, residueTypes,residues,nums2serials are not copied, but only re-referenced |
460 |
* This is because we don't really need to copy the arrays as they should always stay the same between source and destination |
461 |
* If blanckCM is true then CM and CNs will be just initialised to UNKNOWN in CM and blanks in CNs |
462 |
* @return |
463 |
* @param blankCM |
464 |
*/ |
465 |
public ContactMap semiDeepCopy(boolean blankCM){ |
466 |
ContactMap destinationCM = new ContactMap(); |
467 |
// primitives we simply copy |
468 |
destinationCM.t = this.t; |
469 |
destinationCM.effT = this.effT; |
470 |
destinationCM.l = this.l; |
471 |
destinationCM.numObsStandAA = this.numObsStandAA; |
472 |
destinationCM.numContacts = 0; // we wipe it out initially, it stays so unless blankCM is true where is copied from this.numContacts |
473 |
// we don't deep copy here, we can reference the same arrays, they will be always the same for source and destination |
474 |
// TODO should we deep copy the arrays too?? |
475 |
destinationCM.residueNums = this.residueNums; |
476 |
destinationCM.residueTypes = this.residueTypes; |
477 |
destinationCM.residues = this.residues; |
478 |
destinationCM.nums2serials = this.nums2serials; |
479 |
// the only thing we need to deep copy is the ContactMap |
480 |
destinationCM.CM = new EdgeState[l][l]; |
481 |
if (!blankCM){ |
482 |
for (int i=0;i<l;i++) { |
483 |
for (int j=i+1;j<l;j++){ |
484 |
destinationCM.CM[i][j] = this.CM[i][j]; //enum copies should be alright |
485 |
} |
486 |
} |
487 |
destinationCM.numContacts=this.numContacts; |
488 |
} else { |
489 |
for (int i=0;i<l;i++) { |
490 |
for (int j=i+1;j<l;j++){ |
491 |
destinationCM.CM[i][j] = EdgeState.UNKNOWN; |
492 |
} |
493 |
} |
494 |
} |
495 |
// for CNs we create a new object and call the getAllComNbs that gets CNs from the already copied ContactMap array |
496 |
destinationCM.CNs = new HashMap<Integer,HashMap<Integer,TreeMap<Integer,String>>>(); |
497 |
if (!blankCM){ |
498 |
destinationCM.CNs = destinationCM.getAllComNbs(); |
499 |
} |
500 |
return destinationCM; |
501 |
} |
502 |
|
503 |
/** |
504 |
* Semi deep copy from given otherCM to this ContactMap all data member fields |
505 |
* @param otherCM |
506 |
* @param blankCM true if we want a blank ContactMap matrix, false if we want to copy from otherCM.CM |
507 |
*/ |
508 |
public void semiDeepCopyFromOtherCM(ContactMap otherCM, boolean blankCM){ |
509 |
// primitives we simply copy |
510 |
this.t = otherCM.t; |
511 |
this.effT = otherCM.effT; |
512 |
this.l = otherCM.l; |
513 |
this.numObsStandAA = otherCM.l; |
514 |
this.numContacts = 0; |
515 |
// we don't deep copy here, we can reference the same arrays, they will be always the same for source and destination |
516 |
// TODO should we deep copy the arrays too?? |
517 |
this.residueNums = otherCM.residueNums; |
518 |
this.residueTypes = otherCM.residueTypes; |
519 |
this.residues = otherCM.residues; |
520 |
this.nums2serials = otherCM.nums2serials; |
521 |
// the only thing we need to deep copy is the ContactMap |
522 |
this.CM = new EdgeState[l][l]; |
523 |
if (!blankCM){ |
524 |
for (int i=0;i<l;i++) { |
525 |
for (int j=i+1;j<l;j++){ |
526 |
CM[i][j] = otherCM.CM[i][j]; //enum copies should be alright |
527 |
} |
528 |
} |
529 |
this.numContacts=otherCM.numContacts; |
530 |
} else { |
531 |
for (int i=0;i<l;i++) { |
532 |
for (int j=i+1;j<l;j++){ |
533 |
CM[i][j] = EdgeState.UNKNOWN; |
534 |
} |
535 |
} |
536 |
} |
537 |
// for CNs we create a new object and call the getAllComNbs that gets CNs from the already copied ContactMap array |
538 |
this.CNs = new HashMap<Integer,HashMap<Integer,TreeMap<Integer,String>>>(); |
539 |
if (!blankCM){ |
540 |
this.CNs = this.getAllComNbs(); |
541 |
} |
542 |
|
543 |
} |
544 |
|
545 |
/** |
546 |
* To update the numContacts data member, to be used after a ContactMap matrix has been changed in a ContactMap object |
547 |
* |
548 |
*/ |
549 |
public void updateNumContacts(){ |
550 |
this.numContacts=0; |
551 |
for (int i=0;i<l;i++){ |
552 |
for (int j=i+1;j<l;j++) { |
553 |
if (this.CM[i][j].contact()) this.numContacts++; |
554 |
} |
555 |
} |
556 |
} |
557 |
|
558 |
/** |
559 |
* Returns true if this ContactMap and otherCM have exactly the same set of contacts (true/false, disregarding other EdgeStates) |
560 |
* @param otherCM |
561 |
* @return |
562 |
*/ |
563 |
public boolean hasSameContacts (ContactMap otherCM){ |
564 |
boolean coincides = true; |
565 |
for (int i=0;i<l;i++){ |
566 |
for (int j=i+1;j<l;j++) { |
567 |
if ((this.CM[i][j].contact() && !otherCM.CM[i][j].contact()) |
568 |
|| !this.CM[i][j].contact() && otherCM.CM[i][j].contact()) { |
569 |
coincides = false; |
570 |
return coincides; |
571 |
} |
572 |
} |
573 |
} |
574 |
return coincides; |
575 |
} |
576 |
|
577 |
/** |
578 |
* Returns a boolean matrix with the contact map |
579 |
*/ |
580 |
public boolean[][] getBoolMatrix(){ |
581 |
boolean[][] cmbool = new boolean[l][l]; |
582 |
for (int i=0;i<l;i++){ |
583 |
for (int j=0;j<l;j++) { |
584 |
cmbool[i][j]=CM[i][j].contact(); |
585 |
} |
586 |
} |
587 |
return cmbool; |
588 |
} |
589 |
|
590 |
/** |
591 |
* Returns an int (0,1) matrix with the contact map |
592 |
*/ |
593 |
public int[][] getIntMatrix(){ |
594 |
int[][] cmint = new int[l][l]; |
595 |
for (int i=0;i<l;i++){ |
596 |
for (int j=0;j<l;j++) { |
597 |
cmint[i][j]=0; |
598 |
if (CM[i][j].contact()){ |
599 |
cmint[i][j]=1; |
600 |
} |
601 |
} |
602 |
} |
603 |
return cmint; |
604 |
} |
605 |
|
606 |
} |