$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
64 private static int maxCombs = 250;
65
69 private FragmentSpace fragSpace = null;
70
71//------------------------------------------------------------------------------
72
94 Vertex vA, Vertex vB, boolean screenAll)
95 {
96 this(fragSpace, vA, vB, null, screenAll, false, true);
97 }
98
99//------------------------------------------------------------------------------
100
125 Vertex vA, Vertex vB,
126 APMapping fixedRootAPs, boolean screenAll,
127 boolean onlyCompleteMappings, boolean compatibleIfFree)
128 {
129 this.fragSpace = fragSpace;
130 List<AttachmentPoint> needyAPsA = new ArrayList<AttachmentPoint>();
131 if (vA.getGraphOwner()!=null)
132 {
133 List<Vertex> subgraph = new ArrayList<Vertex>();
134 subgraph.add(vA);
135 needyAPsA = vA.getGraphOwner().getInterfaceAPs(subgraph);
136 }
137 List<AttachmentPoint> needyAPsB =
138 new ArrayList<AttachmentPoint>();
139 if (vB.getGraphOwner()!=null)
140 {
141 List<Vertex> subgraph = new ArrayList<Vertex>();
142 subgraph.add(vB);
143 needyAPsB = vB.getGraphOwner().getInterfaceAPs(subgraph);
144 }
145 findAllMappings(vA.getAttachmentPoints(), needyAPsA,
146 vB.getAttachmentPoints(), needyAPsB,
147 fixedRootAPs, screenAll, onlyCompleteMappings, compatibleIfFree);
148 }
149
150//------------------------------------------------------------------------------
151
174 List<AttachmentPoint> lstA,
175 List<AttachmentPoint> needyAPsA,
176 List<AttachmentPoint> lstB,
177 List<AttachmentPoint> needyAPsB,
178 APMapping fixedRootAPs, boolean screenAll,
179 boolean onlyCompleteMappings, boolean compatibleIfFree)
180 {
181 this.fragSpace = fragSpace;
182 findAllMappings(lstA, needyAPsA, lstB, needyAPsB,
183 fixedRootAPs, screenAll, onlyCompleteMappings, compatibleIfFree);
184 }
185
186//------------------------------------------------------------------------------
187
205 private void findAllMappings(List<AttachmentPoint> lstA,
206 List<AttachmentPoint> needyAPsA,
207 List<AttachmentPoint> lstB,
208 List<AttachmentPoint> needyAPsB,
209 APMapping fixedRootAPs, boolean screenAll,
210 boolean onlyCompleteMappings, boolean compatibleIfFree)
211 {
212 // Remove from the lists those APs that have already a mapping
213 List<AttachmentPoint> purgedLstA = new ArrayList<AttachmentPoint>(lstA);
214 if (fixedRootAPs!=null)
215 {
216 purgedLstA.removeAll(fixedRootAPs.keySet());
217 }
218 List<AttachmentPoint> purgedLstB = new ArrayList<AttachmentPoint>(lstB);
219 if (fixedRootAPs!=null)
220 {
221 purgedLstB.removeAll(fixedRootAPs.values());
222 }
223
224 // Map all the compatibilities before choosing a specific mapping
225 LinkedHashMap<AttachmentPoint,List<AttachmentPoint>> apCompatilities =
226 findMappingCompatibileAPs(purgedLstA, purgedLstB,
227 compatibleIfFree, fragSpace);
228
229 // The 'keys' is used just to keep the map keys sorted in a separate list
230 // so that the order is randomized only once, then it is retained.
231 List<AttachmentPoint> keys = new ArrayList<AttachmentPoint>(
232 apCompatilities.keySet());
233 if (fixedRootAPs!=null)
234 {
235 // Since these are constrained we do not need them among the keys
236 // when looking over the combinations of keys
237 keys.removeAll(fixedRootAPs.keySet());
238 }
239
240 // Test if we have enough AP compatibilities to satisfy the constraints
241 Set<AttachmentPoint> doableAPsA = new HashSet<AttachmentPoint>(keys);
242 if (fixedRootAPs!=null)
243 doableAPsA.addAll(fixedRootAPs.keySet());
244 Set<AttachmentPoint> doableAPsB = new HashSet<AttachmentPoint>();
245 apCompatilities.values().stream().forEach(l -> doableAPsB.addAll(l));
246 if (onlyCompleteMappings)
247 {
248 for (AttachmentPoint oldAp : lstA)
249 {
250 if (!needyAPsA.contains(oldAp))
251 needyAPsA.add(oldAp);
252 }
253 for (AttachmentPoint oldAp : lstB)
254 {
255 if (!needyAPsB.contains(oldAp))
256 needyAPsB.add(oldAp);
257 }
258 }
259 Set<AttachmentPoint> mustBeDoableA = new HashSet<AttachmentPoint>(
260 needyAPsA);
261 Set<AttachmentPoint> mustBeDoableB = new HashSet<AttachmentPoint>(
262 needyAPsB);
263 if (fixedRootAPs!=null)
264 {
265 mustBeDoableA.removeAll(fixedRootAPs.keySet());
266 mustBeDoableB.removeAll(fixedRootAPs.values());
267 }
268 if (!doableAPsA.containsAll(mustBeDoableA)
269 || !doableAPsB.containsAll(mustBeDoableB))
270 {
271 return;
272 }
273
274 if (!onlyCompleteMappings)
275 {
276 for (AttachmentPoint oldAp : lstA)
277 {
278 if (oldAp.isAvailableThroughout())
279 {
280 // NB: adding 'null' will allow the presence of AP mappings
281 // where oldAP is intentionally left out of the mapping thus
282 // allowing to let it stay free.
283 if (apCompatilities.containsKey(oldAp))
284 {
285 apCompatilities.get(oldAp).add(null);
286 }
287 }
288 }
289 }
290
291 // Get all possible combinations of compatible AP pairs
292 int currentKey = 0;
293 APMapping currentMapping = new APMapping();
294 if (fixedRootAPs!=null)
295 {
296 currentMapping = fixedRootAPs.clone(); //shallow
297 }
298 // We try the comprehensive approach, but if that is too demanding
299 // and gets stopped, then we run a series of simplified attempts
300 // to hit a decent combination with "lucky shots".
301 Boolean stopped = false;
302 if (keys.size()>0)
303 {
305 currentKey, apCompatilities, currentMapping, allAPMappings,
306 screenAll, maxCombs);
307 } else {
308 // This would have been done by the recursive combiner
309 allAPMappings.add(currentMapping);
310 }
311
312 // Purge according to lists of APs that needs a mapping
313 List<APMapping> toRemove = new ArrayList<APMapping>();
314 for (APMapping c : allAPMappings)
315 {
316 if (!c.containsAllKeys(needyAPsA))
317 {
318 toRemove.add(c);
319 }
320 if (!c.containsAllValues(needyAPsB))
321 {
322 toRemove.add(c);
323 }
324 }
325 allAPMappings.removeAll(toRemove);
326
327 // If we where interrupted, we try to get a proper mapping again.
328 // This time we ignore all the possibilities and try a more direct
329 // approach, which, however, cannot account for all possible combs.
330 if (stopped && allAPMappings.isEmpty())
331 {
332 for (int iTry = 0; iTry < maxCombs; iTry++)
333 {
334 APMapping apMap = new APMapping();
335 List<AttachmentPoint> used =
336 new ArrayList<AttachmentPoint>();
337 List<AttachmentPoint> availKeys =
338 new ArrayList<AttachmentPoint>();
339 availKeys.addAll(needyAPsA);
340 boolean abandon = false;
341 for (int jj=0; jj<needyAPsA.size(); jj++)
342 {
343 AttachmentPoint ap =
345 availKeys);
346 availKeys.remove(ap);
347 List<AttachmentPoint> availPartners =
348 new ArrayList<AttachmentPoint>();
349 availPartners.addAll(apCompatilities.get(ap));
350 boolean done = false;
351 for (int j=0; j<apCompatilities.get(ap).size(); j++)
352 {
353 AttachmentPoint chosenAvail =
355 availPartners);
356 availPartners.remove(chosenAvail);
357 if (used.contains(chosenAvail))
358 {
359 continue;
360 }
361 used.add(chosenAvail);
362 apMap.put(ap,chosenAvail);
363 done = true;
364 break;
365 }
366 if (!done)
367 {
368 abandon = true;
369 break;
370 }
371 }
372
373 if (!abandon)
374 {
375 allAPMappings.add(apMap);
376 break;
377 }
378 }
379 }
380 if (allAPMappings.size() > 0)
383 }
384
385//------------------------------------------------------------------------------
386
402 public static LinkedHashMap<AttachmentPoint,List<AttachmentPoint>>
403 findMappingCompatibileAPs(List<AttachmentPoint> lstA,
404 List<AttachmentPoint> lstB, boolean compatibleIfFree,
406 {
407 LinkedHashMap<AttachmentPoint,List<AttachmentPoint>>
408 apCompatilities = new LinkedHashMap<AttachmentPoint,
409 List<AttachmentPoint>>();
410
411 for (AttachmentPoint oAP : lstA)
412 {
413 for (AttachmentPoint cAP : lstB)
414 {
415 boolean compatible = false;
417 {
418 // Addressing the cases where the APs are used/free
419 // and have src/trg role
420
421 boolean oAPInUse = !oAP.isAvailableThroughout();
422 boolean oAPIsSrc = oAP.isSrcInUserThroughout();
423 boolean oAPIsTrg = oAPInUse && !oAP.isSrcInUserThroughout();
424
425 boolean cAPInUse = !cAP.isAvailableThroughout();
426 boolean cAPIsSrc = cAP.isSrcInUserThroughout();
427 boolean cAPIsTrg = cAPInUse && !cAP.isSrcInUserThroughout();
428
429 // These can be null if AP is not in use!
432
433 APClass oAPcl = oAP.getAPClass();
434 APClass cAPcl = cAP.getAPClass();
435 APClass loAPcl = null;
436 if (loAP!=null)
437 loAPcl = loAP.getAPClass();
438 APClass lcAPcl = null;
439 if (lcAP!=null)
440 lcAPcl = lcAP.getAPClass();
441
442 // TODO: if the vertex is a template, we should
443 // consider the required APs.
444
445 if (oAP.getAPClass().equals(cAP.getAPClass()))
446 {
447 compatible = true;
448 } else {
449 if (!oAPInUse && !cAPInUse && compatibleIfFree)
450 {
451 compatible = true;
452 } else if (!oAPInUse && cAPInUse && compatibleIfFree)
453 {
454 if (cAPIsSrc && oAPcl.isCPMapCompatibleWith(lcAPcl,
455 fragSpace))
456 {
457 compatible = true;
458 } else if (cAPIsTrg && lcAPcl.isCPMapCompatibleWith(
459 oAPcl, fragSpace))
460 {
461 compatible = true;
462 }
463 } else if (oAPInUse && !cAPInUse && compatibleIfFree)
464 {
465 if (oAPIsSrc && cAPcl.isCPMapCompatibleWith(loAPcl,
466 fragSpace))
467 {
468 compatible = true;
469 } else if (oAPIsTrg && loAPcl.isCPMapCompatibleWith(
470 cAPcl, fragSpace))
471 {
472 compatible = true;
473 }
474 } else if (oAPInUse && cAPInUse)
475 {
476 if (oAPIsSrc && cAPIsSrc)
477 {
478 // both are SRC
479 if (oAPcl.isCPMapCompatibleWith(lcAPcl,
480 fragSpace)
481 && cAPcl.isCPMapCompatibleWith(loAPcl,
482 fragSpace))
483 {
484 compatible = true;
485 }
486 } else if ((!oAPIsSrc && cAPIsSrc)
487 || (oAPIsSrc && !cAPIsSrc))
488 {
489 // Since directions will have to be inverted, we
490 // want the APClass compatibility to exists in
491 // both directions on each connection.
492 if (oAPcl.isCPMapCompatibleWith(lcAPcl,
493 fragSpace)
494 && lcAPcl.isCPMapCompatibleWith(oAPcl,
495 fragSpace)
496 && cAPcl.isCPMapCompatibleWith(loAPcl,
497 fragSpace)
498 && loAPcl.isCPMapCompatibleWith(cAPcl,
499 fragSpace))
500 {
501 compatible = true;
502 }
503 } else {
504 // both oAP and cAP are TRG
505 if (lcAPcl.isCPMapCompatibleWith(oAPcl,
506 fragSpace)
507 && loAPcl.isCPMapCompatibleWith(cAPcl,
508 fragSpace))
509 {
510 compatible = true;
511 }
512 }
513 }
514 }
515 } else {
516 compatible = true;
517 }
518 if (compatible)
519 {
520 if (apCompatilities.containsKey(oAP))
521 {
522 apCompatilities.get(oAP).add(cAP);
523 } else {
524 List<AttachmentPoint> lst =
525 new ArrayList<AttachmentPoint>();
526 lst.add(cAP);
527 apCompatilities.put(oAP,lst);
528 }
529 }
530 }
531 }
532 return apCompatilities;
533 }
534
535//------------------------------------------------------------------------------
536
541 public boolean foundMapping()
542 {
543 return !allAPMappings.isEmpty();
544 }
545
546//------------------------------------------------------------------------------
547
557 {
558 return chosenAPMap;
559 }
560
561//------------------------------------------------------------------------------
562
571 public List<APMapping> getAllAPMappings()
572 {
573 return allAPMappings;
574 }
575
576//------------------------------------------------------------------------------
577
578}
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, 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.
static int maxCombs
Maximum number of combinations.
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:7376
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.