001package conexp.fx.core.math;
002
003import java.util.Arrays;
004
005/*
006 * #%L
007 * Concept Explorer FX
008 * %%
009 * Copyright (C) 2010 - 2023 Francesco Kriegel
010 * %%
011 * This program is free software: you can redistribute it and/or modify
012 * it under the terms of the GNU General Public License as
013 * published by the Free Software Foundation, either version 3 of the
014 * License, or (at your option) any later version.
015 * 
016 * This program is distributed in the hope that it will be useful,
017 * but WITHOUT ANY WARRANTY; without even the implied warranty of
018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
019 * GNU General Public License for more details.
020 * 
021 * You should have received a copy of the GNU General Public
022 * License along with this program.  If not, see
023 * <http://www.gnu.org/licenses/gpl-3.0.html>.
024 * #L%
025 */
026
027import java.util.Collection;
028import java.util.HashMap;
029import java.util.HashSet;
030import java.util.Iterator;
031import java.util.Map;
032import java.util.Set;
033import java.util.concurrent.atomic.AtomicBoolean;
034import java.util.function.Function;
035import java.util.function.Predicate;
036
037import org.apache.commons.lang3.NotImplementedException;
038
039import conexp.fx.core.context.Context;
040import conexp.fx.core.context.Implication;
041
042@FunctionalInterface
043public interface SetClosureOperator<M> extends Function<Set<M>, Set<M>> {
044
045  @Override
046  public default Set<M> apply(final Set<M> set) {
047    return closure(set);
048  }
049
050  public Set<M> closure(Set<M> set);
051
052  public default boolean close(final Set<M> set) {
053    return !set.addAll(closure(set));
054  }
055
056  public default boolean isClosed(final Set<M> set) {
057//    return set.containsAll(closure(set));
058    return set.size() == closure(set).size();
059  }
060
061  @SafeVarargs
062  public static <T> SetClosureOperator<T> infimum(final SetClosureOperator<T>... closureOperators) {
063    return infimum(Arrays.asList(closureOperators));
064  }
065
066  public static <T> SetClosureOperator<T> infimum(final Iterable<SetClosureOperator<T>> closureOperators) {
067    return set -> {
068      final Set<T> closure = new HashSet<T>();
069      final Iterator<SetClosureOperator<T>> iterator = closureOperators.iterator();
070      if (iterator.hasNext())
071        closure.addAll(iterator.next().closure(set));
072      while (iterator.hasNext())
073        closure.retainAll(iterator.next().closure(set));
074      return closure;
075    };
076  }
077
078  @SafeVarargs
079  public static <T> SetClosureOperator<T> supremum(final SetClosureOperator<T>... closureOperators) {
080    return supremum(Arrays.asList(closureOperators));
081  }
082
083  public static <T> SetClosureOperator<T> supremum(final Iterable<SetClosureOperator<T>> closureOperators) {
084    return set -> {
085      final Set<T> closure = new HashSet<T>();
086      closure.addAll(set);
087      boolean changed = true;
088      while (changed) {
089        changed = false;
090        for (SetClosureOperator<T> clop : closureOperators)
091          changed |= closure.addAll(clop.closure(closure));
092      }
093      return closure;
094    };
095//    @Override
096//    public final boolean isClosed(final Set<M> set) {
097//      return closureOperators.parallelStream().allMatch(
098//          clop -> clop.isClosed(set));
099//    }
100  }
101
102  public static <G, M> SetClosureOperator<M> fromContext(final Context<G, M> cxt) {
103    return set -> cxt.rowAnd(cxt.colAnd(set));
104  }
105
106  public static <G, M> SetClosureOperator<M>
107      joiningImplications(final Context<G, M> cxt, final Set<M> premises, final Set<M> conclusions) {
108    return set -> {
109      final HashSet<M> pintersection = new HashSet<>(set);
110      pintersection.retainAll(premises);
111      final Set<M> cattributes = cxt.rowAnd(cxt.colAnd(pintersection));
112      cattributes.retainAll(conclusions);
113      final Set<M> closure = new HashSet<>(cattributes);
114      closure.addAll(set);
115      return closure;
116    };
117  }
118
119  public static <G, M, C extends Set<M>> C implicativeClosure(
120      final Collection<Implication<G, M>> implications,
121      final int firstPremiseSize,
122      final boolean includePseudoClosures,
123      final boolean parallel,
124      final boolean bySize,
125      final Function<Set<M>, C> supplier,
126      final Set<M> set) {
127    final C closure = supplier.apply(set);// new HashSet<M>(set);
128    final AtomicBoolean changed = new AtomicBoolean(true);
129    final Predicate<Implication<G, M>> p;
130    if (includePseudoClosures)
131      p = i -> closure.size() > i.getPremise().size() && closure.containsAll(i.getPremise());
132    else
133      p = i -> closure.size() >= i.getPremise().size() && closure.containsAll(i.getPremise());
134    // && (closure.size() < i.getConclusion().size() || !closure.containsAll(i.getConclusion())))
135    if (bySize) {
136      int size;
137      if (firstPremiseSize > 0) {
138        size = closure.size();
139        (parallel ? implications.parallelStream() : implications.stream())
140            .filter(i -> firstPremiseSize <= i.getPremise().size())
141            .filter(p)
142            .sequential()
143            .forEach(i -> closure.addAll(i.getConclusion()));
144        changed.set(closure.size() != size);
145      }
146      while (changed.get()) {
147        size = closure.size();
148        changed.set(false);
149        (parallel ? implications.parallelStream() : implications.stream())
150            .filter(p)
151            .sequential()
152            .forEach(i -> closure.addAll(i.getConclusion()));
153        changed.set(closure.size() != size);
154      }
155    } else {
156      if (firstPremiseSize > 0) {
157        (parallel ? implications.parallelStream() : implications.stream())
158            .filter(i -> firstPremiseSize <= i.getPremise().size())
159            .filter(p)
160            .sequential()
161            .forEach(i -> changed.set(closure.addAll(i.getConclusion()) || changed.get()));
162      }
163      while (changed.get()) {
164        changed.set(false);
165        (parallel ? implications.parallelStream() : implications.stream())
166            .filter(p)
167            .sequential()
168            .forEach(i -> changed.set(closure.addAll(i.getConclusion()) || changed.get()));
169      }
170    }
171    return closure;
172  }
173
174  public static <G, M, C extends Set<M>> SetClosureOperator<M> fromImplications(
175      final Collection<Implication<G, M>> implications,
176      final int firstPremiseSize,
177      final boolean includePseudoClosures,
178      final boolean parallel,
179      final boolean bySize,
180      final Function<Set<M>, C> supplier) {
181    return set -> implicativeClosure(
182        implications,
183        firstPremiseSize,
184        includePseudoClosures,
185        parallel,
186        bySize,
187        supplier,
188        set);
189  }
190
191  public static <G, M> SetClosureOperator<M> fromImplications(
192      final Collection<Implication<G, M>> implications,
193      final int firstPremiseSize,
194      final boolean includePseudoClosures,
195      final boolean parallel) {
196    return fromImplications(implications, firstPremiseSize, includePseudoClosures, parallel, false, HashSet<M>::new);
197  }
198
199  public static <G, M> SetClosureOperator<M> fromImplications(
200      final Collection<Implication<G, M>> implications,
201      final boolean includePseudoClosures,
202      final boolean parallel) {
203    return fromImplications(implications, 0, includePseudoClosures, parallel);
204  }
205
206  public static <G, M> SetClosureOperator<M> fromImplications(final Collection<Implication<G, M>> implications) {
207    return fromImplications(implications, false, true);
208  }
209
210  public static <G, M> SetClosureOperator<M>
211      fromImplicationSetLinClosure(final Collection<Implication<G, M>> implications) {
212    return set -> {
213
214      // Initialization
215      final Map<Implication<G, M>, Integer> count = new HashMap<Implication<G, M>, Integer>();
216      final Map<M, Set<Implication<G, M>>> list = new HashMap<M, Set<Implication<G, M>>>();
217      for (M attribute : set)
218        list.put(attribute, new HashSet<Implication<G, M>>());
219      for (Implication<G, M> implication : implications) {
220        for (M attribute : implication.getPremise())
221          list.put(attribute, new HashSet<Implication<G, M>>());
222        for (M attribute : implication.getConclusion())
223          list.put(attribute, new HashSet<Implication<G, M>>());
224      }
225      // final Multimap<M, Implication<G, M>> list = HashMultimap.create();
226      final Set<M> newdep = new HashSet<M>();
227      final Set<M> update = new HashSet<M>();
228      for (Implication<G, M> implication : implications) {
229        count.put(implication, implication.getPremise().size());
230        if (implication.getPremise().isEmpty())
231          newdep.addAll(implication.getConclusion());
232        for (M attribute : implication.getPremise()) {
233          if (!list.containsKey(attribute))
234            list.put(attribute, new HashSet<Implication<G, M>>());
235          list.get(attribute).add(implication);
236          // list.put(
237          // attribute,
238          // implication);
239        }
240      }
241      newdep.addAll(set);
242      update.addAll(set);
243
244      // Computation
245      while (!update.isEmpty()) {
246        final M attribute = update.iterator().next();
247        update.remove(attribute);
248        for (Implication<G, M> implication : list.get(attribute)) {
249          final Integer previous = count.get(implication);
250          count.put(implication, previous - 1);
251          if (count.get(implication) == 0) {
252            final Set<M> add = new HashSet<M>();
253            add.addAll(implication.getConclusion());
254            add.removeAll(newdep);
255            newdep.addAll(add);
256            update.addAll(add);
257          }
258        }
259      }
260
261      return newdep;
262    };
263  }
264
265  public static <G, M> SetClosureOperator<M>
266      fromImplicationSetDowlingGalier(final Collection<Implication<G, M>> implications) {
267    throw new NotImplementedException("");
268  }
269
270//  public Set<M> closure(final Set<M> attributes) {
271//    final Set<M> closure = new HashSet<M>(attributes);
272//    final Map<Implication<M>, Integer> count = new HashMap<Implication<M>, Integer>();
273//    final Multimap<M, Implication<M>> list = HashMultimap.<M, Implication<M>> create();
274//    for (Implication<M> impl : this) {
275//      final int psize = impl.getPremise().size();
276//      count.put(impl, psize);
277//      if (psize == 0)
278//        closure.addAll(impl.getConclusion());
279//      else
280//        for (M im : impl.getPremise())
281//          list.put(im, impl);
282//    }
283//    List<M> update = new LinkedList<M>(attributes);
284//    while (!update.isEmpty()) {
285//      final M m = update.remove(0);
286//      for (Implication<M> impl : list.get(m)) {
287//        final int newCount = count.get(impl) - 1;
288//        count.put(impl, newCount);
289//        if (newCount == 0) {
290//          final Set<M> add = new HashSet<M>(Sets.difference(impl.getConclusion(), closure));
291//          closure.addAll(add);
292//          update.addAll(add);
293//        }
294//      }
295//    }
296//    return closure;
297//  }
298
299  /**
300   * @param maxCard
301   * @param baseSet
302   * @return a closure operator, whose closed sets are those that do not exceed the given maximal cardinality, or
303   *         contains all elements from the base set.
304   */
305  public static <G, M> SetClosureOperator<M> byMaximalCardinality(final int maxCard, final Collection<M> baseSet) {
306    return new SetClosureOperator<M>() {
307
308      @Override
309      public boolean isClosed(final Set<M> set) {
310        return set.size() <= maxCard || set.containsAll(baseSet);
311      }
312
313      @Override
314      public boolean close(final Set<M> set) {
315        if (isClosed(set))
316          return true;
317        set.addAll(baseSet);
318        return false;
319      }
320
321      @Override
322      public Set<M> closure(final Set<M> set) {
323        if (isClosed(set))
324          return new HashSet<M>(set);
325        return new HashSet<M>(baseSet);
326      }
327
328    };
329  }
330
331  /**
332   * @param minSupp
333   * @param cxt
334   * @return a closure operator, whose closed sets are those that have an extent with at least minSupp elements in the
335   *         given formal context cxt, or contain all elements from the context's codomain.
336   */
337  public static <G, M> SetClosureOperator<M> byMinimalSupport(final int minSupp, final Context<G, M> cxt) {
338    return new SetClosureOperator<M>() {
339
340      @Override
341      public boolean isClosed(final Set<M> set) {
342        return cxt.colAnd(set).size() >= minSupp || set.containsAll(cxt.colHeads());
343      }
344
345      @Override
346      public boolean close(final Set<M> set) {
347        if (isClosed(set))
348          return true;
349        set.addAll(cxt.colHeads());
350        return false;
351      }
352
353      @Override
354      public Set<M> closure(final Set<M> set) {
355        if (isClosed(set))
356          return new HashSet<M>(set);
357        return new HashSet<M>(cxt.colHeads());
358      }
359
360    };
361  }
362
363  /**
364   * @param elements
365   * @param baseSet
366   * @return a closure operator, whose closed sets are those that contain all given elements.
367   */
368  public static <G, M> SetClosureOperator<M> containsAllFrom(final Collection<M> elements, final Set<M> baseSet) {
369    return new SetClosureOperator<M>() {
370
371      @Override
372      public boolean isClosed(final Set<M> set) {
373        return set.containsAll(elements);
374      }
375
376      @Override
377      public boolean close(final Set<M> set) {
378        if (isClosed(set))
379          return true;
380        set.addAll(elements);
381        return false;
382      }
383
384      @Override
385      public Set<M> closure(final Set<M> set) {
386        if (isClosed(set))
387          return new HashSet<M>(set);
388        Set<M> result = new HashSet<M>(set);
389        result.addAll(elements);
390        return result;
391      }
392
393    };
394  }
395
396  /**
397   * @param elements
398   * @param baseSet
399   * @return a closure operator, whose closed sets are those, that are subsets of elements, or contain all elements from
400   *         the baseSet.
401   */
402  public static <G, M> SetClosureOperator<M> isSubsetOf(final Collection<M> elements, final Set<M> baseSet) {
403    return new SetClosureOperator<M>() {
404
405      @Override
406      public boolean isClosed(final Set<M> set) {
407        return elements.containsAll(set);
408      }
409
410      @Override
411      public boolean close(final Set<M> set) {
412        if (isClosed(set))
413          return true;
414        set.addAll(baseSet);
415        return false;
416      }
417
418      @Override
419      public Set<M> closure(final Set<M> set) {
420        if (isClosed(set))
421          return new HashSet<M>(set);
422        return new HashSet<M>(baseSet);
423      }
424
425    };
426  }
427
428  public static <M> SetClosureOperator<M> bottom() {
429    return new SetClosureOperator<M>() {
430
431      @Override
432      public final boolean isClosed(final Set<M> set) {
433        return true;
434      }
435
436      @Override
437      public final boolean close(final Set<M> set) {
438        return true;
439      }
440
441      @Override
442      public final Set<M> closure(final Set<M> set) {
443        return new HashSet<M>(set);
444      }
445    };
446  }
447
448  public static <M> SetClosureOperator<M> top(final Set<M> baseSet) {
449    return new SetClosureOperator<M>() {
450
451      @Override
452      public final boolean isClosed(final Set<M> set) {
453        return set.containsAll(baseSet) && baseSet.containsAll(set);
454      }
455
456      @Override
457      public final boolean close(final Set<M> set) {
458        set.retainAll(baseSet);
459        return !set.addAll(baseSet);
460      }
461
462      @Override
463      public final Set<M> closure(final Set<M> set) {
464        return new HashSet<M>(baseSet);
465      }
466    };
467  }
468
469}