001package conexp.fx.core.algorithm.exploration;
002
003/*
004 * #%L
005 * Concept Explorer FX
006 * %%
007 * Copyright (C) 2010 - 2019 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.Arrays;
026import java.util.Collections;
027import java.util.NoSuchElementException;
028import java.util.Set;
029import java.util.concurrent.Callable;
030import java.util.concurrent.ConcurrentHashMap;
031import java.util.concurrent.ExecutionException;
032import java.util.concurrent.ExecutorService;
033import java.util.stream.Collectors;
034
035import conexp.fx.core.context.Implication;
036import javafx.collections.FXCollections;
037import javafx.collections.MapChangeListener;
038import javafx.collections.ObservableMap;
039
040public class ExpertPool<G, M> implements Expert<G, M> {
041
042  enum Strategy {
043    ANSWER_FROM_FIRST_IDLE("Chooses a random idle expert and returns its/his/her answer."),
044    FIRST_ANSWER_FROM_IDLE("Use answer from fastest idle expert"),
045    FIRST_ANSWER("Use answer from fastest expert"),
046    SOME_CONFIRMS(
047        "Confirm all implications that are confirmed by at least one of the experts, i.e., reject all implications that are rejected by all experts."),
048    ALL_CONFIRM(
049        "Confirm all implications that confirmed by all experts, i.e., reject all implications that are rejected by at least one of the experts.");
050
051    private final String description;
052
053    private Strategy(final String description) {
054      this.description = description;
055    }
056
057    public String getDescription() {
058      return this.description;
059    }
060
061  }
062
063  private final ObservableMap<Expert<G, M>, Boolean> experts = FXCollections.observableMap(new ConcurrentHashMap<>());
064  private final Strategy                             strategy;
065  private final ExecutorService                      exe;
066
067  public ExpertPool(final ExpertPool.Strategy strategy, final ExecutorService executor) {
068    this.strategy = strategy;
069    this.exe = executor;
070    this.experts.addListener((MapChangeListener<Expert<G, M>, Boolean>) change -> {
071      if (change.wasAdded() && change.wasRemoved()) {
072        System.out.println(change.getKey() + " IS NOW " + (change.getValueAdded() ? "IDLE" : "BUSY"));
073      } else if (change.wasAdded()) {
074        System.out.println(change.getKey() + " WAS ADDED AND IS " + (change.getValueAdded() ? "IDLE" : "BUSY"));
075      } else if (change.wasRemoved()) {
076        System.out.println(change.getKey() + " WAS REMOVED AND WAS " + (change.getValueRemoved() ? "IDLE" : "BUSY"));
077      }
078    });
079  }
080
081  @Override
082  public Set<CounterExample<G, M>> getCounterExamples(Implication<G, M> implication) throws InterruptedException {
083    switch (strategy) {
084    case ANSWER_FROM_FIRST_IDLE:
085      return getIdleExpert().getCounterExamples(implication);
086    case FIRST_ANSWER_FROM_IDLE:
087      try {
088        return exe.invokeAny(
089            this.experts
090                .entrySet()
091                .parallelStream()
092                .filter(entry -> entry.getValue().equals(true))
093                .map(entry -> entry.getKey())
094                .map(expert -> (Callable<Set<CounterExample<G, M>>>) () -> {
095                  try {
096                    return expert.getCounterExamples(implication);
097                  } catch (Exception e) {
098                    return null;
099                  }
100                })
101                .collect(Collectors.toSet()));
102      } catch (ExecutionException e) {
103        return null;
104      }
105    case FIRST_ANSWER:
106      try {
107        return exe.invokeAny(
108            this.experts.keySet().parallelStream().map(expert -> (Callable<Set<CounterExample<G, M>>>) () -> {
109              try {
110                return expert.getCounterExamples(implication);
111              } catch (Exception e) {
112                return null;
113              }
114            }).collect(Collectors.toSet()));
115      } catch (ExecutionException e) {
116        return null;
117      }
118    case SOME_CONFIRMS:
119      if (experts.keySet().parallelStream().map(expert -> {
120        try {
121          return expert.getCounterExamples(implication);
122        } catch (Exception __) {
123          return null;
124        }
125
126      }).anyMatch(cex -> cex.isEmpty()))
127        return Collections.emptySet();
128    case ALL_CONFIRM:
129      return experts.keySet().parallelStream().map(expert -> {
130        try {
131          return expert.getCounterExamples(implication);
132        } catch (Exception __) {
133          return null;
134        }
135      }).flatMap(Set::parallelStream).collect(Collectors.toSet());
136    default:
137      return Collections.emptySet();
138    }
139
140  }
141
142  @SafeVarargs
143  public final void add(final Expert<G, M>... experts) {
144    add(Arrays.asList(experts));
145  }
146
147  public final void add(final Iterable<Expert<G, M>> experts) {
148    experts.forEach(expert -> this.experts.put(expert, true));
149  }
150
151  @SafeVarargs
152  public final void remove(final Expert<G, M>... experts) {
153    remove(Arrays.asList(experts));
154  }
155
156  public final void remove(final Iterable<Expert<G, M>> experts) {
157    experts.forEach(expert -> this.experts.remove(expert));
158  }
159
160  public final boolean isIdle(final Expert<G, M> expert) {
161    return this.experts.get(expert);
162  }
163
164  public final boolean allIdle() {
165    return !this.experts.values().contains(false);
166  }
167
168  public final boolean someIdle() {
169    return this.experts.values().contains(true);
170  }
171
172  public final void setBusy(final Expert<G, M> expert) {
173    if (!this.experts.containsKey(expert))
174      throw new IllegalArgumentException();
175    this.experts.put(expert, false);
176  }
177
178  public final void setIdle(final Expert<G, M> expert) {
179    if (!this.experts.containsKey(expert))
180      throw new IllegalArgumentException();
181    this.experts.put(expert, true);
182  }
183
184  public final Expert<G, M> getIdleExpert() throws NoSuchElementException {
185    return this.experts
186        .entrySet()
187        .parallelStream()
188        .filter(entry -> entry.getValue().equals(true))
189        .map(entry -> entry.getKey())
190        .findAny()
191        .get();
192  }
193
194  public final Strategy getStrategy() {
195    return this.strategy;
196  }
197
198}