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