$darkmode
DENOPTIM
FragsCombinationIterator.java
Go to the documentation of this file.
1/*
2 * DENOPTIM
3 * Copyright (C) 2019 Marco Foscato <marco.foscato@uib.no>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package denoptim.fragspace;
20
21import java.util.ArrayList;
22import java.util.HashMap;
23import java.util.Iterator;
24import java.util.Map;
25import java.util.NoSuchElementException;
26import java.util.logging.Level;
27
28import denoptim.constants.DENOPTIMConstants;
29import denoptim.exception.DENOPTIMException;
30import denoptim.graph.APClass;
31import denoptim.graph.AttachmentPoint;
32import denoptim.graph.DGraph;
33import denoptim.graph.SymmetricAPs;
34import denoptim.graph.SymmetricVertexes;
35import denoptim.graph.Vertex;
36import denoptim.utils.GraphUtils;
37
38
49{
53 private boolean finished = false;
54
59
63 private ArrayList<IdFragmentAndAP> allSrcAps =
64 new ArrayList<IdFragmentAndAP>();
65
71 private ArrayList<IdFragmentAndAP> actvSrcAps =
72 new ArrayList<IdFragmentAndAP>();
73
77 private Map<IdFragmentAndAP,ArrayList<IdFragmentAndAP>> candFragsPerAP =
78 new HashMap<IdFragmentAndAP,ArrayList<IdFragmentAndAP>>();
79
83 private ArrayList<Integer> nextIds = new ArrayList<Integer>();
84
88 private ArrayList<Integer> totCandsPerAP = new ArrayList<Integer>();
89
93 private int totCombs = 0;
94
98 private int numbGenCombs = 0;
99
100 private String EOL = DENOPTIMConstants.EOL;
101
106
107
108//------------------------------------------------------------------------------
109
117 //TODO-V3+: this usage of the getBuildingBlockId recorded inside the
118 // vertex means that any change to the space of building blocks
119 // that alter the list of the BBs before the value returns by
120 // getBuildingBlockId has the potential of generating a mismatch
121 // between the apId in the graph and those obtained from the library.
122
125 {
126 this.rootGraph = rootGraph;
127 this.settings = settings;
128
129 // Identify all candidate source APs: free APs on the root graph/vertex.
130 // In case of symmetry, store only the first among the symmetric
131 // vertices in the graph, and the first AP among the symmetric APs
132 // in a vertex
133 for (Vertex v : this.rootGraph.getVertexList())
134 {
135 long vIdx = v.getVertexId();
136 int vMolId = v.getBuildingBlockId();
137 Vertex.BBType vMolTyp = v.getBuildingBlockType();
138
139 // deal with symmetric sets of vertices
140 boolean keepThisVertex = true;
141 if ((settings.enforceSymmetry() && this.rootGraph.hasSymmetricAP())
143 {
144 keepThisVertex = false;
145 boolean isInSymSet = false;
146 Iterator<SymmetricVertexes> it = this.rootGraph.getSymSetsIterator();
147 while (it.hasNext())
148 {
149 SymmetricVertexes ss = it.next();
150 if (ss.contains(v))
151 {
152 isInSymSet = true;
153 if (ss.indexOf(v) == 0)
154 {
155 keepThisVertex = true;
156 }
157 break;
158 }
159 }
160 if (!isInSymSet)
161 {
162 keepThisVertex = true;
163 }
164 }
165 if (!keepThisVertex)
166 {
167 continue;
168 }
169
170 for (AttachmentPoint ap : v.getAttachmentPoints())
171 {
172 if (!ap.isAvailable())
173 continue;
174
175 // NB: index not ID!!!
176 int apIdx = v.getIndexOfAP(ap);
177 // deal with symmetric sets of APs on this vertex
178 boolean keepThisAP = true;
181 && v.hasSymmetricAP())
182 {
183 keepThisAP = false;
184 boolean isInSymSet = false;
185 for (SymmetricAPs ss : v.getSymmetricAPSets())
186 {
187 if (ss.contains(ap))
188 {
190 .imposeSymmetryOnAPsOfClass(ap.getAPClass()))
191 {
192 continue;
193 }
194
195 isInSymSet = true;
196 if (ss.indexOf(ap) == 0)
197 {
198 keepThisAP = true;
199 }
200 break;
201 }
202 }
203 if (!isInSymSet)
204 {
205 keepThisAP = true;
206 }
207 }
208 if (!keepThisAP)
209 {
210 continue;
211 }
212
213 IdFragmentAndAP idFragAp = new IdFragmentAndAP(vIdx, vMolId,
214 vMolTyp, apIdx, -1, -1);
215 allSrcAps.add(idFragAp);
216 }
217 }
218
219 // Collect all possibilities (frags, caps, entry) for each free AP
220 for (IdFragmentAndAP candSrcAp : allSrcAps)
221 {
222 Vertex.BBType fTyp = candSrcAp.getVertexMolType();
223 int fIdx = candSrcAp.getVertexMolId();
224 int apId = candSrcAp.getApId();
226 .getVertexFromLibrary(fTyp, fIdx);
227 APClass srcApCls = frag.getAttachmentPoints().get(apId).getAPClass();
228
229 // Create data structure for candidates
230 ArrayList<IdFragmentAndAP> candsForThisSrc =
231 new ArrayList<IdFragmentAndAP>();
232
233 // Get all compatible fragments
234 ArrayList<IdFragmentAndAP> compatFragAps =
236 srcApCls);
237
238 for (IdFragmentAndAP compatApId : compatFragAps)
239 {
240 long vid = GraphUtils.getUniqueVertexIndex();
241 IdFragmentAndAP trgFrgAp = new IdFragmentAndAP(vid, //vertexId
242 compatApId.getVertexMolId(), //MolId,
244 compatApId.getApId(), //Ap index
245 -1, //noVSym
246 -1);//noAPSym
247
248 candsForThisSrc.add(trgFrgAp);
249 }
250
251 // Get other possible destinies for the free AP
252 // NOTE: in principle there should be only ONE capping group, but
253 // this is made to work also in case of more than one group.
255 .getAPClassOfCappingVertex(srcApCls);
256 if (capApCls != null)
257 {
258 // Use of capping groups
259 // NOTE: in principle there should be only ONE capping group,
260 // but this is made to work also in case of more than
261 // one group.
262 ArrayList<Integer> capGrpIds = settings.getFragmentSpace()
264 for (Integer i : capGrpIds)
265 {
266 long vid = GraphUtils.getUniqueVertexIndex();
267 IdFragmentAndAP trgFrgAp = new IdFragmentAndAP(vid,//vertxId
268 i,//MolId,
270 0,//AP index
271 -1,//noVSym
272 -1);//noAPSym
273 candsForThisSrc.add(trgFrgAp);
274 }
275 }
276 else
277 {
278 // No capping, so can we also leave the AP empty?
280 .contains(srcApCls))
281 {
282 // An empty pointer is used to represent the possibility of
283 // having no fragment
284 IdFragmentAndAP trgFrgAp = new IdFragmentAndAP();
285 candsForThisSrc.add(trgFrgAp);
286 }
287 }
288
289 // Store info on this src AP
290 if (candsForThisSrc.size() > 0)
291 {
292 // Store this FragAp as an active src Ap
293 actvSrcAps.add(candSrcAp);
294
295 // Store the reference to the candidates
296 candFragsPerAP.put(candSrcAp, candsForThisSrc);
297
298 // Store the size of the set of candidates
299 totCandsPerAP.add(candFragsPerAP.get(candSrcAp).size());
300
301 // Initialize index
302 nextIds.add(0);
303 }
304 }
305
306 StringBuilder sb = new StringBuilder();
307 sb.append("Initializing iterator over combs of frags:"+EOL);
308 for (IdFragmentAndAP candSrcAp : allSrcAps)
309 {
310 sb.append(" -> Possibilities for "+candSrcAp+EOL);
311 if (candFragsPerAP.containsKey(candSrcAp))
312 for (IdFragmentAndAP i : candFragsPerAP.get(candSrcAp))
313 sb.append(" ---> " + i+EOL);
314 }
315 settings.getLogger().log(Level.FINE, sb.toString());
316
317 // Calculate to total number of combinations
318 boolean emptySets = true;
319 for (int curSrcApIdx=0; curSrcApIdx<actvSrcAps.size(); curSrcApIdx++)
320 {
321 if (totCandsPerAP.get(curSrcApIdx) > 0)
322 {
323 emptySets = false;
324 break;
325 }
326 }
327 if (!emptySets)
328 {
329 totCombs = 1;
330 for (int srcApIdx=0; srcApIdx<actvSrcAps.size(); srcApIdx++)
331 {
332 int factor;
333 if (totCandsPerAP.get(srcApIdx) <= 1)
334 {
335 factor = 1;
336 }
337 else
338 {
339 factor = totCandsPerAP.get(srcApIdx);
340 }
341 totCombs = totCombs * factor;
342 }
343 }
344 else
345 {
346 finished = true;
347 }
348 }
349
350//------------------------------------------------------------------------------
351
359 public void setStartingPoint(ArrayList<Integer> nextIds)
360 {
361 this.nextIds = nextIds;
362
363 // Check for completion
364 finished = true;
365 for (int i=0; i<nextIds.size(); i++)
366 {
367 if (nextIds.get(i)+1 < totCandsPerAP.get(i))
368 {
369 finished = false;
370 break;
371 }
372 }
373 }
374
375//------------------------------------------------------------------------------
376
381 public ArrayList<Integer> getNextIds()
382 {
383 return nextIds;
384 }
385
386//------------------------------------------------------------------------------
387
394 public FragsCombination next() throws NoSuchElementException
395 {
396 if (finished)
397 {
398 throw new NoSuchElementException();
399 }
400
401 settings.getLogger().log(Level.FINE, "Calculating next frag combination "
402 + "(id:" + nextIds + ", size:" + totCandsPerAP + ")");
403
404 // While defining the combination of fragments we also keep track
405 // of which incoming fragments are related by symmetry (i.e., we
406 // set the symmetric set ID for each incoming vertex) to allow
407 // an easy update of the graph's SymmetricSet list
408
409 FragsCombination currentComb = new FragsCombination();
410 ArrayList<Integer> currentIds = new ArrayList<Integer>();
411 String msg = "";
412 for (int curSrcApIdx=0; curSrcApIdx<actvSrcAps.size(); curSrcApIdx++)
413 {
414 IdFragmentAndAP src = actvSrcAps.get(curSrcApIdx);
415 src.setVrtSymSetId(curSrcApIdx);
416 ArrayList<IdFragmentAndAP> locCandsList = candFragsPerAP.get(src);
417 for (IdFragmentAndAP fap : locCandsList)
418 {
419 fap.setVrtSymSetId(curSrcApIdx);
420 }
421 int locTotCands = candFragsPerAP.get(src).size();
422 int locCurCandId = nextIds.get(curSrcApIdx);
423
424 msg = "Setting frag for AP " + curSrcApIdx + " (" + src + ")";
425 settings.getLogger().log(Level.FINER, msg);
426
427 // Decide whether the candidate for the current position
428 // has to be changed
429 boolean changeCurIdx = true;
430 for (int fIdx=curSrcApIdx+1; fIdx<actvSrcAps.size(); fIdx++)
431 {
432 if (nextIds.get(fIdx) < totCandsPerAP.get(fIdx))
433 {
434 changeCurIdx = false;
435 break;
436 }
437 }
438
439 // Non-last srcAP
440 if ((curSrcApIdx+1) < actvSrcAps.size())
441 {
442 if (!changeCurIdx)
443 {
444 msg = "Non-last src AP; fixed on " + locCurCandId;
445 settings.getLogger().log(Level.FINER, msg);
446 currentIds.add(locCurCandId);
447 currentComb.put(src,locCandsList.get(locCurCandId));
448 }
449 else
450 {
451 if (locCurCandId+1 < locTotCands)
452 {
453 // increment the ids for this src AP
454 locCurCandId++;
455 nextIds.set(curSrcApIdx,locCurCandId);
456 // and restart all the idx on all srcAP that follow
457 StringBuilder sb = new StringBuilder();
458 for (int futureSrcAp=curSrcApIdx+1;
459 futureSrcAp<actvSrcAps.size(); futureSrcAp++)
460 {
461 sb.append("Restart idx on src AP " + futureSrcAp + EOL);
462 nextIds.set(futureSrcAp,0);
463 }
464
465 sb.append("Non-last src AP; new idx = " + locCurCandId);
466 settings.getLogger().log(Level.FINER, sb.toString());
467
468 currentIds.add(locCurCandId);
469 currentComb.put(src,locCandsList.get(locCurCandId));
470 }
471 else
472 {
473 // Restart idx for this src AP and increment the
474 // closest activable index
475 // To do this, we increase the local id so it goes
476 // beyind the maximum (it's going to be zeroed later)...
477 locCurCandId++;
478 nextIds.set(curSrcApIdx,locCurCandId);
479 // ...and rerun the previous iteration on srcAPs
480 msg = "Step BACK from src AP " + curSrcApIdx;
481 settings.getLogger().log(Level.FINER,msg);
482 currentIds.remove(currentIds.size()-1);
483 curSrcApIdx = curSrcApIdx - 2; //it get +1 from loop
484 }
485 }
486 }
487 // last srcAP; always try to change the candidate for this AP
488 else if ((curSrcApIdx+1) == actvSrcAps.size())
489 {
490 msg = "Last src AP; new idx = " + locCurCandId;
491 settings.getLogger().log(Level.FINER,msg);
492
493 currentIds.add(locCurCandId);
494 currentComb.put(src,locCandsList.get(locCurCandId));
495 locCurCandId++;
496 nextIds.set(curSrcApIdx,locCurCandId);
497
498 // Check for completion
499 finished = true;
500 for (int i=0; i<nextIds.size(); i++)
501 {
502 int nextId = nextIds.get(i)+1;
503 if (i == curSrcApIdx)
504 {
505 nextId = nextId - 1; //has been incremented above
506 }
507 if (nextId < totCandsPerAP.get(i))
508 {
509 finished = false;
510 break;
511 }
512 }
513 msg = "Check completion on " + nextIds + ": " + finished;
514 settings.getLogger().log(Level.FINER,msg);
515 }
516 }
517
518 numbGenCombs++;
519
520 StringBuilder sb = new StringBuilder();
521 sb.append("Combination before applying symmetry: ");
522 for (Map.Entry<IdFragmentAndAP,IdFragmentAndAP> entry : currentComb.entrySet())
523 {
524 sb.append(" "+entry);
525 }
526 settings.getLogger().log(Level.FINER,sb.toString());
527
528 // Project selection onto symmetric positions
531 {
532 Map<IdFragmentAndAP,IdFragmentAndAP> pairsToAdd =
533 new HashMap<IdFragmentAndAP,IdFragmentAndAP>();
534
535 // deal with symmetric APs within the same vertex
536 for (IdFragmentAndAP srcFrgAp : currentComb.keySet())
537 {
538 long srcVrtId = srcFrgAp.getVertexId();
539 int srcApId = srcFrgAp.getApId();
540 Vertex v = this.rootGraph.getVertexWithId(srcVrtId);
541 AttachmentPoint srcAP = v.getAttachmentPoints().get(srcApId);
542 if (!v.hasSymmetricAP())
543 {
544 continue;
545 }
546 for (SymmetricAPs ss : v.getSymmetricAPSets())
547 {
548 if (ss.contains(srcAP))
549 {
552 {
553 continue;
554 }
555
556 IdFragmentAndAP trgFrgAp = currentComb.get(srcFrgAp);
557 for (int i=1; i<ss.size(); i++)
558 {
559 AttachmentPoint symmSrcAP = ss.get(i);
560 if (!symmSrcAP.isAvailable())
561 {
562 continue;
563 }
564
565 IdFragmentAndAP symSrcFrgAp = new IdFragmentAndAP(
566 srcVrtId, v.getBuildingBlockId(),
568 symmSrcAP.getIndexInOwner(),
569 srcFrgAp.getVrtSymSetId(),
570 -1);
571
572 IdFragmentAndAP symTrgFrgAp = new IdFragmentAndAP(
574 trgFrgAp.getVertexMolId(),
575 trgFrgAp.getVertexMolType(),
576 trgFrgAp.getApId(),
577 trgFrgAp.getVrtSymSetId(),
578 -1);
579
580 pairsToAdd.put(symSrcFrgAp, symTrgFrgAp);
581 }
582 break;
583 }
584 }
585 }
586
587 // Store projection onto symmetric APs of the same vertex
588 for (Map.Entry<IdFragmentAndAP,IdFragmentAndAP> entry :
589 pairsToAdd.entrySet())
590 {
591 currentComb.put(entry.getKey(), entry.getValue());
592 }
593 pairsToAdd.clear();
594
595 // deal with symmetric vertices
596 if (this.rootGraph.hasSymmetricAP())
597 {
598 for (IdFragmentAndAP srcFrgAp : currentComb.keySet())
599 {
600 long srcVrtId = srcFrgAp.getVertexId();
601 Vertex srcVrt = this.rootGraph.getVertexWithId(srcVrtId);
602 Iterator<SymmetricVertexes> it =
603 this.rootGraph.getSymSetsIterator();
604 while (it.hasNext())
605 {
606 SymmetricVertexes ss = it.next();
607 if (ss.contains(srcVrt))
608 {
609 IdFragmentAndAP trgFrgAp = currentComb.get(srcFrgAp);
610 for (int i=1; i<ss.size(); i++)
611 {
612 Vertex symVertex = ss.get(i);
613
614 IdFragmentAndAP symSrcFrgAp =
615 new IdFragmentAndAP(
616 symVertex.getVertexId(),
617 symVertex.getBuildingBlockId(),
618 symVertex.getBuildingBlockType(),
619 srcFrgAp.getApId(),
620 srcFrgAp.getVrtSymSetId(),
621 -1);
622
623 IdFragmentAndAP symTrgFrgAp =
624 new IdFragmentAndAP(
626 trgFrgAp.getVertexMolId(),
627 trgFrgAp.getVertexMolType(),
628 trgFrgAp.getApId(),
629 trgFrgAp.getVrtSymSetId(),
630 -1);
631
632 pairsToAdd.put(symSrcFrgAp,symTrgFrgAp);
633 }
634 break;
635 }
636 }
637 }
638
639 // Store projection onto symmetric vertices
640 for (Map.Entry<IdFragmentAndAP,IdFragmentAndAP> entry :
641 pairsToAdd.entrySet())
642 {
643 currentComb.put(entry.getKey(), entry.getValue());
644 }
645 pairsToAdd.clear();
646 }
647 }
648
649 StringBuilder sb2 = new StringBuilder();
650 sb2.append("Final combination of frags/APs: ");
651 for (Map.Entry<IdFragmentAndAP,IdFragmentAndAP> entry : currentComb.entrySet())
652 {
653 sb2.append(" "+entry);
654 }
655 settings.getLogger().log(Level.FINER, sb2.toString());
656
657 return currentComb;
658 }
659
660//------------------------------------------------------------------------------
661
667 public boolean hasNext()
668 {
669 if (finished)
670 {
671 return false;
672 }
673 else
674 {
675 return true;
676 }
677 }
678
679//------------------------------------------------------------------------------
680
685 public int getNumRootAPs()
686 {
687
688 return actvSrcAps.size();
689 }
690
691//------------------------------------------------------------------------------
692
698 public int getTotNumbCombs()
699 {
700 return totCombs;
701 }
702
703//------------------------------------------------------------------------------
704
711 {
712 return numbGenCombs;
713 }
714
715//------------------------------------------------------------------------------
716
722 public Map<IdFragmentAndAP,ArrayList<IdFragmentAndAP>> getCandidatesMap()
723 {
724 return candFragsPerAP;
725 }
726
727//------------------------------------------------------------------------------
728
734 public ArrayList<Integer> getSizesOfCandidateSets()
735 {
736 return totCandsPerAP;
737 }
738
739//------------------------------------------------------------------------------
740
741}
General set of constants used in DENOPTIM.
static final String EOL
new line character
Vertex getVertexFromLibrary(Vertex.BBType bbType, int bbIdx)
Returns a clone of the requested building block.
ArrayList< Integer > getCappingGroupsWithAPClass(APClass capApCls)
APClass getAPClassOfCappingVertex(APClass srcApClass)
boolean imposeSymmetryOnAPsOfClass(APClass apClass)
Checks if the symmetry settings impose use of symmetry on attachment points of the given AP class.
ArrayList< IdFragmentAndAP > getFragAPsCompatibleWithClass(APClass aPC1)
Returns the list of attachment points found in the fragment space and that are compatible with a give...
Parameters defining the fragment space.
boolean enforceSymmetry
Flag enforcing constitutional symmetry.
boolean symmetryConstraints
Flag for application of selected constitutional symmetry constraints.
Data structure identifying a combination of one or more pairs of attachment points located on specifi...
FragsCombination next()
Generate the next combination of fragments.
FragmentSpaceParameters settings
Parameters defining the fragment space.
Map< IdFragmentAndAP, ArrayList< IdFragmentAndAP > > getCandidatesMap()
ArrayList< IdFragmentAndAP > actvSrcAps
List of attachment points on the root graph (source APs) for which there are one or more candidate de...
ArrayList< Integer > totCandsPerAP
Sizes of the candidates set for each source AP.
ArrayList< Integer > getNextIds()
Returns the list of indeces used to calculate the next iteration.
void setStartingPoint(ArrayList< Integer > nextIds)
Sets the starting point for the iterator.
ArrayList< Integer > nextIds
Set of indeces for the next iteration.
Map< IdFragmentAndAP, ArrayList< IdFragmentAndAP > > candFragsPerAP
Map of all possible matches for each source AP.
int numbGenCombs
Current number of generated combinations.
DGraph rootGraph
The root graph (may be a single fragment or an extended graph)
ArrayList< IdFragmentAndAP > allSrcAps
List of all attachment points on the root graph (source APs)
boolean finished
Flag defining whether at list one more combination can be produced.
FragsCombinationIterator(FragmentSpaceParameters settings, DGraph rootGraph)
Constructs a new factory for generating all the combination of fragments that can be attached on a gi...
Data structure containing information that identifies a single AP of a vertex/fragment.
An attachment point (AP) is a possibility to attach a Vertex onto the vertex holding the AP (i....
APClass getAPClass()
Returns the Attachment Point class.
boolean isAvailable()
Check availability of this attachment point.
Container for the list of vertices and the edges that connect them.
Definition: DGraph.java:102
Vertex getVertexWithId(long vid)
Searches for a vertex with the given identifier.
Definition: DGraph.java:2582
Iterator< SymmetricVertexes > getSymSetsIterator()
Get an iterator for the sets of symmetrically related vertices.
Definition: DGraph.java:324
List< Vertex > getVertexList()
Definition: DGraph.java:719
boolean hasSymmetricAP()
Definition: DGraph.java:286
A collection of AttachmentPoints that are related by a relation that we call "symmetry",...
A collection of Vertexs that are related by a relation that we call "symmetry", even though this clas...
A vertex is a data structure that has an identity and holds a list of AttachmentPoints.
Definition: Vertex.java:61
int getBuildingBlockId()
Returns the index of the building block that should correspond to the position of the building block ...
Definition: Vertex.java:284
boolean hasSymmetricAP()
Definition: Vertex.java:541
Vertex.BBType getBuildingBlockType()
Definition: Vertex.java:298
abstract List< AttachmentPoint > getAttachmentPoints()
abstract List< SymmetricAPs > getSymmetricAPSets()
Logger getLogger()
Get the name of the program specific logger.
Utilities for graphs.
Definition: GraphUtils.java:40
static synchronized long getUniqueVertexIndex()
Unique counter for the number of graph vertices generated.
Definition: GraphUtils.java:97
The type of building block.
Definition: Vertex.java:86