$darkmode
DENOPTIM
APMapFinder.java
Go to the documentation of this file.
1/*
2 * DENOPTIM
3 * Copyright (C) 2022 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.HashSet;
23import java.util.LinkedHashMap;
24import java.util.List;
25import java.util.Set;
26
27import denoptim.graph.APClass;
28import denoptim.graph.APMapping;
29import denoptim.graph.AttachmentPoint;
30import denoptim.graph.DGraph;
31import denoptim.graph.Vertex;
32
40public class APMapFinder
41{
49
55 private List<APMapping> allAPMappings = new ArrayList<APMapping>();
56
60 public static final int DEFAULT_MAX_COMBS = 250;
61
70
74 private FragmentSpace fragSpace = null;
75
76//------------------------------------------------------------------------------
77
99 Vertex vA, Vertex vB, boolean screenAll)
100 {
101 this(fragSpace, vA, vB, null, screenAll, false, true);
102 }
103
104//------------------------------------------------------------------------------
105
130 Vertex vA, Vertex vB,
131 APMapping fixedRootAPs, boolean screenAll,
132 boolean onlyCompleteMappings, boolean compatibleIfFree)
133 {
134 this(fragSpace, vA, vB, fixedRootAPs, screenAll, onlyCompleteMappings,
135 compatibleIfFree, DEFAULT_MAX_COMBS);
136 }
137
138//------------------------------------------------------------------------------
139
149 Vertex vA, Vertex vB,
150 APMapping fixedRootAPs, boolean screenAll,
151 boolean onlyCompleteMappings, boolean compatibleIfFree,
152 int maxCombs)
153 {
154 this.fragSpace = fragSpace;
155 this.maxCombs = maxCombs;
156 List<AttachmentPoint> needyAPsA = new ArrayList<AttachmentPoint>();
157 if (vA.getGraphOwner()!=null)
158 {
159 List<Vertex> subgraph = new ArrayList<Vertex>();
160 subgraph.add(vA);
161 needyAPsA = vA.getGraphOwner().getInterfaceAPs(subgraph);
162 }
163 List<AttachmentPoint> needyAPsB =
164 new ArrayList<AttachmentPoint>();
165 if (vB.getGraphOwner()!=null)
166 {
167 List<Vertex> subgraph = new ArrayList<Vertex>();
168 subgraph.add(vB);
169 needyAPsB = vB.getGraphOwner().getInterfaceAPs(subgraph);
170 }
171 findAllMappings(vA.getAttachmentPoints(), needyAPsA,
172 vB.getAttachmentPoints(), needyAPsB,
173 fixedRootAPs, screenAll, onlyCompleteMappings, compatibleIfFree);
174 }
175
176//------------------------------------------------------------------------------
177
200 List<AttachmentPoint> lstA,
201 List<AttachmentPoint> needyAPsA,
202 List<AttachmentPoint> lstB,
203 List<AttachmentPoint> needyAPsB,
204 APMapping fixedRootAPs, boolean screenAll,
205 boolean onlyCompleteMappings, boolean compatibleIfFree)
206 {
207 this(fragSpace, lstA, needyAPsA, lstB, needyAPsB, fixedRootAPs,
208 screenAll, onlyCompleteMappings, compatibleIfFree,
210 }
211
212//------------------------------------------------------------------------------
213
222 List<AttachmentPoint> lstA,
223 List<AttachmentPoint> needyAPsA,
224 List<AttachmentPoint> lstB,
225 List<AttachmentPoint> needyAPsB,
226 APMapping fixedRootAPs, boolean screenAll,
227 boolean onlyCompleteMappings, boolean compatibleIfFree,
228 int maxCombs)
229 {
230 this.fragSpace = fragSpace;
231 this.maxCombs = maxCombs;
232 findAllMappings(lstA, needyAPsA, lstB, needyAPsB,
233 fixedRootAPs, screenAll, onlyCompleteMappings, compatibleIfFree);
234 }
235
236//------------------------------------------------------------------------------
237
255 private void findAllMappings(List<AttachmentPoint> lstA,
256 List<AttachmentPoint> needyAPsA,
257 List<AttachmentPoint> lstB,
258 List<AttachmentPoint> needyAPsB,
259 APMapping fixedRootAPs, boolean screenAll,
260 boolean onlyCompleteMappings, boolean compatibleIfFree)
261 {
262 // Remove from the lists those APs that have already a mapping
263 List<AttachmentPoint> purgedLstA = new ArrayList<AttachmentPoint>(lstA);
264 if (fixedRootAPs!=null)
265 {
266 purgedLstA.removeAll(fixedRootAPs.keySet());
267 }
268 List<AttachmentPoint> purgedLstB = new ArrayList<AttachmentPoint>(lstB);
269 if (fixedRootAPs!=null)
270 {
271 purgedLstB.removeAll(fixedRootAPs.values());
272 }
273
274 // Map all the compatibilities before choosing a specific mapping
275 LinkedHashMap<AttachmentPoint,List<AttachmentPoint>> apCompatilities =
276 findMappingCompatibileAPs(purgedLstA, purgedLstB,
277 compatibleIfFree, fragSpace);
278
279 // The 'keys' is used just to keep the map keys sorted in a separate list
280 // so that the order is randomized only once, then it is retained.
281 List<AttachmentPoint> keys = new ArrayList<AttachmentPoint>(
282 apCompatilities.keySet());
283 if (fixedRootAPs!=null)
284 {
285 // Since these are constrained we do not need them among the keys
286 // when looking over the combinations of keys
287 keys.removeAll(fixedRootAPs.keySet());
288 }
289
290 // Test if we have enough AP compatibilities to satisfy the constraints
291 Set<AttachmentPoint> doableAPsA = new HashSet<AttachmentPoint>(keys);
292 if (fixedRootAPs!=null)
293 doableAPsA.addAll(fixedRootAPs.keySet());
294 Set<AttachmentPoint> doableAPsB = new HashSet<AttachmentPoint>();
295 apCompatilities.values().stream().forEach(l -> doableAPsB.addAll(l));
296 if (onlyCompleteMappings)
297 {
298 for (AttachmentPoint oldAp : lstA)
299 {
300 if (!needyAPsA.contains(oldAp))
301 needyAPsA.add(oldAp);
302 }
303 for (AttachmentPoint oldAp : lstB)
304 {
305 if (!needyAPsB.contains(oldAp))
306 needyAPsB.add(oldAp);
307 }
308 }
309 Set<AttachmentPoint> mustBeDoableA = new HashSet<AttachmentPoint>(
310 needyAPsA);
311 Set<AttachmentPoint> mustBeDoableB = new HashSet<AttachmentPoint>(
312 needyAPsB);
313 if (fixedRootAPs!=null)
314 {
315 mustBeDoableA.removeAll(fixedRootAPs.keySet());
316 mustBeDoableB.removeAll(fixedRootAPs.values());
317 }
318 if (!doableAPsA.containsAll(mustBeDoableA)
319 || !doableAPsB.containsAll(mustBeDoableB))
320 {
321 return;
322 }
323
324 if (!onlyCompleteMappings)
325 {
326 for (AttachmentPoint oldAp : lstA)
327 {
328 if (oldAp.isAvailableThroughout())
329 {
330 // NB: adding 'null' will allow the presence of AP mappings
331 // where oldAP is intentionally left out of the mapping thus
332 // allowing to let it stay free.
333 if (apCompatilities.containsKey(oldAp))
334 {
335 apCompatilities.get(oldAp).add(null);
336 }
337 }
338 }
339 }
340
341 // Get all possible combinations of compatible AP pairs
342 int currentKey = 0;
343 APMapping currentMapping = new APMapping();
344 if (fixedRootAPs!=null)
345 {
346 currentMapping = fixedRootAPs.clone(); //shallow
347 }
348 // We try the comprehensive approach, but if that is too demanding
349 // and gets stopped, then we run a series of simplified attempts
350 // to hit a decent combination with "lucky shots".
351 Boolean stopped = false;
352 if (keys.size()>0)
353 {
355 currentKey, apCompatilities, currentMapping, allAPMappings,
356 screenAll, maxCombs);
357 } else {
358 // This would have been done by the recursive combiner
359 allAPMappings.add(currentMapping);
360 }
361
362 // Purge according to lists of APs that needs a mapping
363 List<APMapping> toRemove = new ArrayList<APMapping>();
364 for (APMapping c : allAPMappings)
365 {
366 if (!c.containsAllKeys(needyAPsA))
367 {
368 toRemove.add(c);
369 }
370 if (!c.containsAllValues(needyAPsB))
371 {
372 toRemove.add(c);
373 }
374 }
375 allAPMappings.removeAll(toRemove);
376
377 // If we where interrupted, we try to get a proper mapping again.
378 // This time we ignore all the possibilities and try a more direct
379 // approach, which, however, cannot account for all possible combs.
380 if (stopped && allAPMappings.isEmpty())
381 {
382 for (int iTry = 0; iTry < maxCombs; iTry++)
383 {
384 APMapping apMap = new APMapping();
385 List<AttachmentPoint> used =
386 new ArrayList<AttachmentPoint>();
387 List<AttachmentPoint> availKeys =
388 new ArrayList<AttachmentPoint>();
389 availKeys.addAll(needyAPsA);
390 boolean abandon = false;
391 for (int jj=0; jj<needyAPsA.size(); jj++)
392 {
393 AttachmentPoint ap =
395 availKeys);
396 availKeys.remove(ap);
397 List<AttachmentPoint> availPartners =
398 new ArrayList<AttachmentPoint>();
399 availPartners.addAll(apCompatilities.get(ap));
400 boolean done = false;
401 for (int j=0; j<apCompatilities.get(ap).size(); j++)
402 {
403 AttachmentPoint chosenAvail =
405 availPartners);
406 availPartners.remove(chosenAvail);
407 if (used.contains(chosenAvail))
408 {
409 continue;
410 }
411 used.add(chosenAvail);
412 apMap.put(ap,chosenAvail);
413 done = true;
414 break;
415 }
416 if (!done)
417 {
418 abandon = true;
419 break;
420 }
421 }
422
423 if (!abandon)
424 {
425 allAPMappings.add(apMap);
426 break;
427 }
428 }
429 }
430 if (allAPMappings.size() > 0)
433 }
434
435//------------------------------------------------------------------------------
436
452 public static LinkedHashMap<AttachmentPoint,List<AttachmentPoint>>
453 findMappingCompatibileAPs(List<AttachmentPoint> lstA,
454 List<AttachmentPoint> lstB, boolean compatibleIfFree,
456 {
457 LinkedHashMap<AttachmentPoint,List<AttachmentPoint>>
458 apCompatilities = new LinkedHashMap<AttachmentPoint,
459 List<AttachmentPoint>>();
460
461 for (AttachmentPoint oAP : lstA)
462 {
463 for (AttachmentPoint cAP : lstB)
464 {
465 boolean compatible = false;
467 {
468 // Addressing the cases where the APs are used/free
469 // and have src/trg role
470
471 boolean oAPInUse = !oAP.isAvailableThroughout();
472 boolean oAPIsSrc = oAP.isSrcInUserThroughout();
473 boolean oAPIsTrg = oAPInUse && !oAP.isSrcInUserThroughout();
474
475 boolean cAPInUse = !cAP.isAvailableThroughout();
476 boolean cAPIsSrc = cAP.isSrcInUserThroughout();
477 boolean cAPIsTrg = cAPInUse && !cAP.isSrcInUserThroughout();
478
479 // These can be null if AP is not in use!
482
483 APClass oAPcl = oAP.getAPClass();
484 APClass cAPcl = cAP.getAPClass();
485 APClass loAPcl = null;
486 if (loAP!=null)
487 loAPcl = loAP.getAPClass();
488 APClass lcAPcl = null;
489 if (lcAP!=null)
490 lcAPcl = lcAP.getAPClass();
491
492 // TODO: if the vertex is a template, we should
493 // consider the required APs.
494
495 if (oAP.getAPClass().equals(cAP.getAPClass()))
496 {
497 compatible = true;
498 } else {
499 if (!oAPInUse && !cAPInUse && compatibleIfFree)
500 {
501 compatible = true;
502 } else if (!oAPInUse && cAPInUse && compatibleIfFree)
503 {
504 if (cAPIsSrc && oAPcl.isCPMapCompatibleWith(lcAPcl,
505 fragSpace))
506 {
507 compatible = true;
508 } else if (cAPIsTrg && lcAPcl.isCPMapCompatibleWith(
509 oAPcl, fragSpace))
510 {
511 compatible = true;
512 }
513 } else if (oAPInUse && !cAPInUse && compatibleIfFree)
514 {
515 if (oAPIsSrc && cAPcl.isCPMapCompatibleWith(loAPcl,
516 fragSpace))
517 {
518 compatible = true;
519 } else if (oAPIsTrg && loAPcl.isCPMapCompatibleWith(
520 cAPcl, fragSpace))
521 {
522 compatible = true;
523 }
524 } else if (oAPInUse && cAPInUse)
525 {
526 if (oAPIsSrc && cAPIsSrc)
527 {
528 // both are SRC
529 if (oAPcl.isCPMapCompatibleWith(lcAPcl,
530 fragSpace)
531 && cAPcl.isCPMapCompatibleWith(loAPcl,
532 fragSpace))
533 {
534 compatible = true;
535 }
536 } else if ((!oAPIsSrc && cAPIsSrc)
537 || (oAPIsSrc && !cAPIsSrc))
538 {
539 // Since directions will have to be inverted, we
540 // want the APClass compatibility to exists in
541 // both directions on each connection.
542 if (oAPcl.isCPMapCompatibleWith(lcAPcl,
543 fragSpace)
544 && lcAPcl.isCPMapCompatibleWith(oAPcl,
545 fragSpace)
546 && cAPcl.isCPMapCompatibleWith(loAPcl,
547 fragSpace)
548 && loAPcl.isCPMapCompatibleWith(cAPcl,
549 fragSpace))
550 {
551 compatible = true;
552 }
553 } else {
554 // both oAP and cAP are TRG
555 if (lcAPcl.isCPMapCompatibleWith(oAPcl,
556 fragSpace)
557 && loAPcl.isCPMapCompatibleWith(cAPcl,
558 fragSpace))
559 {
560 compatible = true;
561 }
562 }
563 }
564 }
565 } else {
566 compatible = true;
567 }
568 if (compatible)
569 {
570 if (apCompatilities.containsKey(oAP))
571 {
572 apCompatilities.get(oAP).add(cAP);
573 } else {
574 List<AttachmentPoint> lst =
575 new ArrayList<AttachmentPoint>();
576 lst.add(cAP);
577 apCompatilities.put(oAP,lst);
578 }
579 }
580 }
581 }
582 return apCompatilities;
583 }
584
585//------------------------------------------------------------------------------
586
591 public boolean foundMapping()
592 {
593 return !allAPMappings.isEmpty();
594 }
595
596//------------------------------------------------------------------------------
597
607 {
608 return chosenAPMap;
609 }
610
611//------------------------------------------------------------------------------
612
621 public List<APMapping> getAllAPMappings()
622 {
623 return allAPMappings;
624 }
625
626//------------------------------------------------------------------------------
627
628}
An utility class to encapsulate the search for an AttachmentPoint-AttachmentPoint mapping.
APMapping getChosenAPMapping()
Returns the AttachmentPoint-AttachmentPoint mapping chosen among the possible mappings.
APMapping chosenAPMap
The chosen AttachmentPoint-AttachmentPoint mapping.
List< APMapping > allAPMappings
The collection of all AttachmentPoint-AttachmentPoint mappings that have been found.
APMapFinder(FragmentSpace fragSpace, Vertex vA, Vertex vB, APMapping fixedRootAPs, boolean screenAll, boolean onlyCompleteMappings, boolean compatibleIfFree, int maxCombs)
Constructor that launches the search for a mapping between the AttachmentPoints on the first vertex t...
APMapFinder(FragmentSpace fragSpace, List< AttachmentPoint > lstA, List< AttachmentPoint > needyAPsA, List< AttachmentPoint > lstB, List< AttachmentPoint > needyAPsB, APMapping fixedRootAPs, boolean screenAll, boolean onlyCompleteMappings, boolean compatibleIfFree)
Constructor that launches the search for a mapping between the AttachmentPoints on two lists.
static LinkedHashMap< AttachmentPoint, List< AttachmentPoint > > findMappingCompatibileAPs(List< AttachmentPoint > lstA, List< AttachmentPoint > lstB, boolean compatibleIfFree, FragmentSpace fragSpace)
Compares the AttachmentPoint of two lists searching for all the APs of the second list that are "comp...
boolean foundMapping()
Returns true if any mapping has been found.
List< APMapping > getAllAPMappings()
Returns all AttachmentPoint-AttachmentPoint mapping found.
APMapFinder(FragmentSpace fragSpace, List< AttachmentPoint > lstA, List< AttachmentPoint > needyAPsA, List< AttachmentPoint > lstB, List< AttachmentPoint > needyAPsB, APMapping fixedRootAPs, boolean screenAll, boolean onlyCompleteMappings, boolean compatibleIfFree, int maxCombs)
Constructor that launches the search for a mapping between the AttachmentPoints on two lists.
int maxCombs
Maximum number of combinations.
static final int DEFAULT_MAX_COMBS
Default maximum number of combinations when none is specified.
FragmentSpace fragSpace
Program-specific fragment space.
APMapFinder(FragmentSpace fragSpace, Vertex vA, Vertex vB, APMapping fixedRootAPs, boolean screenAll, boolean onlyCompleteMappings, boolean compatibleIfFree)
Constructor that launches the search for a mapping between the AttachmentPoints on the first vertex t...
void findAllMappings(List< AttachmentPoint > lstA, List< AttachmentPoint > needyAPsA, List< AttachmentPoint > lstB, List< AttachmentPoint > needyAPsB, APMapping fixedRootAPs, boolean screenAll, boolean onlyCompleteMappings, boolean compatibleIfFree)
Searches for mappings between the AttachmentPoints on the two lists.
APMapFinder(FragmentSpace fragSpace, Vertex vA, Vertex vB, boolean screenAll)
Constructor that launches the search for a mapping between the AttachmentPoints on the first vertex t...
Class defining a space of building blocks.
Randomizer getRandomizer()
Returns the program-specific randomizer that is associated with this program-specific fragment space.
boolean useAPclassBasedApproach()
Check usage of APClass-based approach, i.e., uses attachment points with annotated data (i....
Utility class for the fragment space.
static boolean recursiveCombiner(List< AttachmentPoint > keys, int currentKey, Map< AttachmentPoint, List< AttachmentPoint > > possibilities, APMapping combination, List< APMapping > completeCombinations, boolean screenAll, int maxCombs)
Search for all possible combinations of compatible APs.
boolean isCPMapCompatibleWith(APClass other, FragmentSpace fragSpace)
Check compatibility as defined in the compatibility matrix considering this AP as source and the othe...
Definition: APClass.java:483
Class representing a mapping between attachment points (APs).
Definition: APMapping.java:42
APMapping clone()
Shallow cloning.
Definition: APMapping.java:140
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 isAvailableThroughout()
Check availability of this attachment point throughout the graph level, i.e., check also across the i...
AttachmentPoint getLinkedAPThroughout()
Gets the attachment point (AP) that is connected to this AP via the edge user or in any edge user tha...
List< AttachmentPoint > getInterfaceAPs(List< Vertex > subGraphB)
Searches for all AttachmentPoints that represent the interface between a subgraph,...
Definition: DGraph.java:7817
A vertex is a data structure that has an identity and holds a list of AttachmentPoints.
Definition: Vertex.java:61
DGraph getGraphOwner()
Returns the graph this vertex belongs to or null.
Definition: Vertex.java:851
abstract List< AttachmentPoint > getAttachmentPoints()
public< T > T randomlyChooseOne(Collection< T > c)
Chooses one member among the given collection.