/*
* SPDX-FileCopyrightText: 2021-2023 The Refinery Authors
*
* SPDX-License-Identifier: EPL-2.0
*/
package tools.refinery.store.reasoning.internal;
import tools.refinery.store.adapter.AbstractModelAdapterBuilder;
import tools.refinery.store.dse.transition.DesignSpaceExplorationBuilder;
import tools.refinery.store.dse.transition.objectives.Objective;
import tools.refinery.store.dse.transition.objectives.Objectives;
import tools.refinery.store.model.ModelStore;
import tools.refinery.store.model.ModelStoreBuilder;
import tools.refinery.store.query.ModelQueryBuilder;
import tools.refinery.store.query.dnf.Dnf;
import tools.refinery.store.query.dnf.FunctionalQuery;
import tools.refinery.store.query.dnf.Query;
import tools.refinery.store.query.dnf.RelationalQuery;
import tools.refinery.store.reasoning.ReasoningBuilder;
import tools.refinery.store.reasoning.interpretation.PartialInterpretation;
import tools.refinery.store.reasoning.lifting.DnfLifter;
import tools.refinery.store.reasoning.literal.Concreteness;
import tools.refinery.store.reasoning.literal.Modality;
import tools.refinery.store.reasoning.refinement.DefaultStorageRefiner;
import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner;
import tools.refinery.store.reasoning.refinement.PartialModelInitializer;
import tools.refinery.store.reasoning.refinement.StorageRefiner;
import tools.refinery.store.reasoning.representation.AnyPartialSymbol;
import tools.refinery.store.reasoning.translator.AnyPartialSymbolTranslator;
import tools.refinery.store.reasoning.translator.PartialRelationTranslator;
import tools.refinery.store.representation.AnySymbol;
import tools.refinery.store.representation.Symbol;
import tools.refinery.store.statecoding.StateCoderBuilder;
import java.util.*;
public class ReasoningBuilderImpl extends AbstractModelAdapterBuilder
implements ReasoningBuilder {
private final DnfLifter lifter = new DnfLifter();
private final PartialQueryRewriter queryRewriter = new PartialQueryRewriter(lifter);
private Set requiredInterpretations = Set.of(Concreteness.values());
private final Map translators = new LinkedHashMap<>();
private final Map> symbolInterpreters =
new LinkedHashMap<>();
private final Map> symbolRefiners =
new LinkedHashMap<>();
private final Map> registeredStorageRefiners = new LinkedHashMap<>();
private final List initializers = new ArrayList<>();
private final List objectives = new ArrayList<>();
@Override
public ReasoningBuilder requiredInterpretations(Collection requiredInterpretations) {
this.requiredInterpretations = Set.copyOf(requiredInterpretations);
return this;
}
@Override
public ReasoningBuilder partialSymbol(AnyPartialSymbolTranslator translator) {
var partialSymbol = translator.getPartialSymbol();
var oldConfiguration = translators.put(partialSymbol, translator);
if (oldConfiguration != null && oldConfiguration != translator) {
throw new IllegalArgumentException("Duplicate configuration for symbol: " + partialSymbol);
}
return this;
}
@Override
public ReasoningBuilder storageRefiner(Symbol symbol, StorageRefiner.Factory refiner) {
checkNotConfigured();
if (registeredStorageRefiners.put(symbol, refiner) != null) {
throw new IllegalArgumentException("Duplicate representation refiner for symbol: " + symbol);
}
return this;
}
@Override
public ReasoningBuilder initializer(PartialModelInitializer initializer) {
checkNotConfigured();
initializers.add(initializer);
return this;
}
@Override
public ReasoningBuilder objective(Objective objective) {
checkNotConfigured();
objectives.add(objective);
return this;
}
@Override
public Query lift(Modality modality, Concreteness concreteness, Query query) {
return lifter.lift(modality, concreteness, query);
}
@Override
public RelationalQuery lift(Modality modality, Concreteness concreteness, RelationalQuery query) {
return lifter.lift(modality, concreteness, query);
}
@Override
public FunctionalQuery lift(Modality modality, Concreteness concreteness, FunctionalQuery query) {
return lifter.lift(modality, concreteness, query);
}
@Override
public Dnf lift(Modality modality, Concreteness concreteness, Dnf dnf) {
return lifter.lift(modality, concreteness, dnf);
}
@Override
protected void doConfigure(ModelStoreBuilder storeBuilder) {
storeBuilder.symbols(ReasoningAdapterImpl.NODE_COUNT_SYMBOL);
storeBuilder.tryGetAdapter(StateCoderBuilder.class)
.ifPresent(stateCoderBuilder -> stateCoderBuilder.exclude(ReasoningAdapterImpl.NODE_COUNT_SYMBOL));
for (var translator : translators.values()) {
translator.configure(storeBuilder);
if (translator instanceof PartialRelationTranslator relationConfiguration) {
doConfigure(storeBuilder, relationConfiguration);
} else {
throw new IllegalArgumentException("Unknown partial symbol translator %s for partial symbol %s"
.formatted(translator, translator.getPartialSymbol()));
}
}
storeBuilder.symbols(registeredStorageRefiners.keySet());
var queryBuilder = storeBuilder.getAdapter(ModelQueryBuilder.class);
queryBuilder.rewriter(queryRewriter);
if (!objectives.isEmpty()) {
storeBuilder.tryGetAdapter(DesignSpaceExplorationBuilder.class)
.ifPresent(dseBuilder -> dseBuilder.objective(Objectives.sum(objectives)));
}
}
private void doConfigure(ModelStoreBuilder storeBuilder, PartialRelationTranslator relationConfiguration) {
var partialRelation = relationConfiguration.getPartialRelation();
queryRewriter.addRelationRewriter(partialRelation, relationConfiguration.getRewriter());
var interpretationFactory = relationConfiguration.getInterpretationFactory();
interpretationFactory.configure(storeBuilder, requiredInterpretations);
symbolInterpreters.put(partialRelation, interpretationFactory);
var refiner = relationConfiguration.getInterpretationRefiner();
if (refiner != null) {
symbolRefiners.put(partialRelation, refiner);
}
}
@Override
public ReasoningStoreAdapterImpl doBuild(ModelStore store) {
return new ReasoningStoreAdapterImpl(store, requiredInterpretations,
Collections.unmodifiableMap(symbolInterpreters), Collections.unmodifiableMap(symbolRefiners),
getStorageRefiners(store), Collections.unmodifiableList(initializers));
}
private Map> getStorageRefiners(ModelStore store) {
var symbols = store.getSymbols();
var storageRefiners = new LinkedHashMap>(symbols.size());
for (var symbol : symbols) {
var refiner = registeredStorageRefiners.remove(symbol);
if (refiner == null) {
if (symbol.arity() == 0) {
// Arity-0 symbols don't need a default refiner, because they are unaffected by object
// creation/removal. Only a custom refiner ({@code refiner != null}) might need to update them.
continue;
}
// By default, copy over all affected tuples on object creation and remove all affected tuples on
// object removal.
refiner = DefaultStorageRefiner.factory();
}
storageRefiners.put(symbol, refiner);
}
if (!registeredStorageRefiners.isEmpty()) {
throw new IllegalArgumentException("Unused storage refiners: " + registeredStorageRefiners.keySet());
}
return Collections.unmodifiableMap(storageRefiners);
}
}