001package conexp.fx.core.algorithm.nextclosure;
002
003/*
004 * #%L
005 * Concept Explorer FX
006 * %%
007 * Copyright (C) 2010 - 2023 Francesco Kriegel
008 * %%
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as
011 * published by the Free Software Foundation, either version 3 of the
012 * License, or (at your option) any later version.
013 * 
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 * 
019 * You should have received a copy of the GNU General Public
020 * License along with this program.  If not, see
021 * <http://www.gnu.org/licenses/gpl-3.0.html>.
022 * #L%
023 */
024
025import java.util.HashSet;
026import java.util.Iterator;
027import java.util.Set;
028import java.util.function.Function;
029
030import com.google.common.collect.Lists;
031import com.google.common.collect.Sets;
032import com.google.common.collect.UnmodifiableIterator;
033
034import conexp.fx.core.collections.Either;
035import conexp.fx.core.collections.Pair;
036import conexp.fx.core.collections.setlist.SetList;
037import conexp.fx.core.collections.setlist.SetList.LecticOrder;
038import conexp.fx.core.context.Concept;
039import conexp.fx.core.context.Implication;
040import conexp.fx.core.context.MatrixContext;
041import conexp.fx.core.math.SetClosureOperator;
042
043public class NextClosure {
044
045  public static <G, M> Iterable<Concept<G, M>> intents(final MatrixContext<G, M> cxt) {
046    final Function<Set<M>, Concept<G, M>> clop = set -> {
047      final Set<G> extent = cxt.colAnd(set);
048      final Set<M> intent = cxt.rowAnd(extent);
049      return new Concept<G, M>(extent, intent);
050    };
051    return enumerate(cxt.colHeads(), clop.apply(new HashSet<M>()), clop, concept -> concept.getIntent());
052  }
053
054  public static <G, M> Iterable<Either<Concept<G, M>, Implication<G, M>>> implications(final MatrixContext<G, M> cxt) {
055    final Set<Implication<G, M>> implications = Sets.newHashSet();
056    final SetClosureOperator<M> clop = SetClosureOperator.fromImplications(implications, true, true);
057    final Function<Set<M>, Either<Concept<G, M>, Implication<G, M>>> clop2 = set -> {
058      final Set<M> quasiIntent = clop.closure(set);
059      final Set<G> extent = cxt.colAnd(quasiIntent);
060      final Set<M> intent = cxt.rowAnd(extent);
061      if (quasiIntent.size() != intent.size()) {
062        intent.removeAll(quasiIntent);
063        final Implication<G, M> implication = new Implication<G, M>(quasiIntent, intent, extent);
064        implications.add(implication);
065        return Either.<Concept<G, M>, Implication<G, M>> ofRight(implication);
066      } else
067        return Either.<Concept<G, M>, Implication<G, M>> ofLeft(new Concept<G, M>(extent, intent));
068    };
069    return enumerate(
070        cxt.colHeads(),
071        clop2.apply(new HashSet<M>()),
072        clop2,
073        e -> e.x().isPresent() ? e.x().get().getIntent() : e.y().get().getPremise());
074  }
075
076  public static <G, M> Pair<Set<Concept<G, M>>, Set<Implication<G, M>>>
077      conceptsAndImplications(final MatrixContext<G, M> cxt) {
078    final Set<Concept<G, M>> concepts = Sets.newHashSet();
079    final Set<Implication<G, M>> implications = Sets.newHashSet();
080    enumerate(cxt.colHeads(), SetClosureOperator.fromImplications(implications, true, true)).forEach(quasiIntent -> {
081      final Set<G> extent = cxt.colAnd(quasiIntent);
082      final Set<M> intent = cxt.rowAnd(extent);
083      if (quasiIntent.size() != intent.size()) {
084        intent.removeAll(quasiIntent);
085        implications.add(new Implication<G, M>(quasiIntent, intent, extent));
086      } else
087        concepts.add(new Concept<G, M>(extent, intent));
088    });
089    return Pair.of(concepts, implications);
090  }
091
092  public static <T> Iterable<Set<T>> enumerate(final SetList<T> base, final SetClosureOperator<T> clop) {
093    return enumerate(base, new HashSet<T>(), clop::closure, t -> t);
094  }
095
096  public static <T, U> Iterable<U> enumerate(
097      final SetList<T> base,
098      final U first,
099      final Function<Set<T>, U> clop,
100      final Function<U, Set<T>> inverse) {
101    return new Iterable<U>() {
102
103      public final Iterator<U> iterator() {
104
105        return new UnmodifiableIterator<U>() {
106
107          private final LecticOrder<T> lecticOrder = base.getLecticOrder();
108          private U                    nextClosure = first;
109          private boolean              isFirst     = true;
110
111          public final boolean hasNext() {
112            return isFirst || inverse.apply(nextClosure).size() < base.size();
113          }
114
115          public final U next() {
116            if (isFirst)
117              isFirst = false;
118            else if (inverse.apply(nextClosure).size() < base.size()) {
119              for (T m : Lists.reverse(base)) {
120                final U s = clop.apply(lecticOrder.oplus(inverse.apply(nextClosure), m));
121                if (lecticOrder.isSmaller(inverse.apply(nextClosure), inverse.apply(s), m)) {
122                  nextClosure = s;
123                  return nextClosure;
124                }
125              }
126              throw new RuntimeException();
127            }
128            return nextClosure;
129          }
130        };
131      }
132    };
133  }
134}