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}