001package conexp.fx.core.algorithm.nextclosures; 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.Set; 027import java.util.concurrent.ExecutionException; 028import java.util.concurrent.ExecutorService; 029import java.util.concurrent.Executors; 030import java.util.concurrent.Future; 031import java.util.function.Consumer; 032import java.util.function.Supplier; 033 034import conexp.fx.core.collections.Collections3; 035import conexp.fx.core.collections.Pair; 036import conexp.fx.core.context.Concept; 037import conexp.fx.core.context.Context; 038import conexp.fx.core.context.Implication; 039import conexp.fx.core.math.SetClosureOperator; 040import conexp.fx.gui.ConExpFX; 041import conexp.fx.gui.dataset.FCADataset; 042import conexp.fx.gui.task.TimeTask; 043import conexp.fx.gui.util.Platform2; 044 045public final class NextClosures2C { 046 047 public static final <G, M> Pair<Set<Concept<G, M>>, Set<Implication<G, M>>> compute( 048 final Context<G, M> cxt, 049 final ExecutorService executor, 050 final Consumer<Concept<G, M>> conceptConsumer, 051 final Consumer<Implication<G, M>> implicationConsumer, 052 final Consumer<String> updateStatus, 053 final Consumer<Double> updateProgress, 054 final Supplier<Boolean> isCancelled, 055 final SetClosureOperator<M> constraint) { 056 final NextClosuresState<G, M, Set<M>> result = NextClosuresState.withHashSets(cxt.colHeads()); 057 result.candidates.clear(); 058 final HashSet<M> firstCandidate = new HashSet<M>(); 059 constraint.close(firstCandidate); 060 final int firstCardinality = firstCandidate.size(); 061 result.cardinality = firstCardinality; 062 result.candidates.put(firstCandidate, firstCardinality); 063// final ClosureOperator<M> clop = ClosureOperator.fromImplications(result.implications, true, true); 064 final SetClosureOperator<M> sup = SetClosureOperator.supremum(SetClosureOperator.fromContext(cxt), constraint); 065 final int maxCardinality = cxt.colHeads().size(); 066 for (; result.cardinality <= maxCardinality; result.cardinality++) { 067 try { 068 if (isCancelled.get()) 069 break; 070 } catch (Exception __) {} 071 final double q = ((double) result.cardinality) / ((double) maxCardinality); 072 final int p = (int) (100d * q); 073 updateStatus.accept("current cardinality: " + result.cardinality + "/" + maxCardinality + " (" + p + "%)"); 074 updateProgress.accept(q); 075 final Set<Future<?>> futures = Collections3.newConcurrentHashSet(); 076 result.candidates.keySet().parallelStream().filter(c -> c.size() == result.cardinality).forEach(candidate -> { 077 futures.add(executor.submit(() -> { 078 final Set<M> closure = 079 SetClosureOperator 080 .supremum( 081 SetClosureOperator 082 .fromImplications(result.implications, result.candidates.get(candidate), true, true), 083 constraint) 084 .closure(candidate); 085 if (closure.size() == candidate.size()) { 086 final Set<M> candidateII = sup.closure(candidate); 087 final Set<G> candidateI = cxt.colAnd(candidate); 088 if (result.isNewIntent(candidateII)) { 089 final Concept<G, M> concept = new Concept<G, M>(candidateI, new HashSet<M>(candidateII)); 090 result.concepts.add(concept); 091 conceptConsumer.accept(concept); 092 result.addNewCandidates(candidateII); 093 } 094 if (candidateII.size() != candidate.size()) { 095 candidateII.removeAll(candidate); 096 final Implication<G, M> implication = new Implication<G, M>(candidate, candidateII, candidateI); 097 result.implications.add(implication); 098 implicationConsumer.accept(implication); 099 } 100 } else { 101 result.candidates.put(closure, result.cardinality); 102 } 103 result.candidates.remove(candidate); 104 })); 105 }); 106 for (Future<?> future : futures) 107 try { 108 future.get(); 109 } catch (InterruptedException | ExecutionException e) { 110 e.printStackTrace(); 111 } 112 } 113 updateStatus 114 .accept(result.concepts.size() + " concepts, and " + result.implications.size() + " implications found"); 115 return result.getResultAndDispose(); 116 } 117 118 public static final <G, M> Pair<Set<Concept<G, M>>, Set<Implication<G, M>>> 119 compute(final Context<G, M> cxt, final ExecutorService executor, final SetClosureOperator<M> constraint) { 120 return compute( 121 cxt, 122 executor, 123 __ -> {}, 124 __ -> {}, 125 System.out::println, 126 System.out::println, 127 () -> false, 128 constraint); 129 } 130 131 public static final <G, M> Pair<Set<Concept<G, M>>, Set<Implication<G, M>>> 132 compute(final Context<G, M> cxt, final int cores, final SetClosureOperator<M> constraint) { 133 if (cores > Runtime.getRuntime().availableProcessors()) 134 throw new IllegalArgumentException( 135 "Requested pool size is too large. VM has only " + Runtime.getRuntime().availableProcessors() 136 + " available cpus, thus a thread pool with " + cores + " cores cannot be used here."); 137// final ThreadPoolExecutor tpe = 138// new ThreadPoolExecutor(cores, cores, 1000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); 139// tpe.prestartAllCoreThreads(); 140 final ExecutorService tpe = Executors.newWorkStealingPool(cores); 141 final Pair<Set<Concept<G, M>>, Set<Implication<G, M>>> result = compute(cxt, tpe, constraint); 142// tpe.purge(); 143 tpe.shutdown(); 144 return result; 145 } 146 147 public static final <G, M> Pair<Set<Concept<G, M>>, Set<Implication<G, M>>> 148 compute(final Context<G, M> cxt, final SetClosureOperator<M> constraint) { 149 return compute(cxt, Runtime.getRuntime().availableProcessors() - 1, constraint); 150 } 151 152 public static final <G, M> TimeTask<?> createTask(FCADataset<G, M> dataset, final SetClosureOperator<M> constraint) { 153 return new TimeTask<Void>(dataset, "NextClosures") { 154 155 @Override 156 protected Void call() throws Exception { 157 updateProgress(0d, 1d); 158 if (isCancelled()) 159 return null; 160 compute( 161 dataset.context.getSelection(), 162 ConExpFX.instance.executor.tpe, 163 concept -> Platform2.runOnFXThread(() -> dataset.concepts.add(concept)), 164 implication -> Platform2.runOnFXThread(() -> dataset.implications.add(implication)), 165 // dataset.concepts::add, 166 // dataset.implications::add, 167 status -> updateMessage(status), 168 progress -> updateProgress(progress, 1d), 169 () -> isCancelled(), 170 constraint); 171 updateProgress(1d, 1d); 172 return null; 173 } 174 }; 175 } 176 177}