All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.semanticweb.rulewerk.reasoner.vlog.VLogReasoner Maven / Gradle / Ivy

The newest version!
package org.semanticweb.rulewerk.reasoner.vlog;

/*
 * #%L
 * Rulewerk VLog Reasoner Support
 * %%
 * Copyright (C) 2018 - 2020 Rulewerk Developers
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.Validate;
import org.semanticweb.rulewerk.core.exceptions.IncompatiblePredicateArityException;
import org.semanticweb.rulewerk.core.exceptions.ReasonerStateException;
import org.semanticweb.rulewerk.core.exceptions.RulewerkRuntimeException;
import org.semanticweb.rulewerk.core.model.api.DataSource;
import org.semanticweb.rulewerk.core.model.api.DataSourceDeclaration;
import org.semanticweb.rulewerk.core.model.api.Fact;
import org.semanticweb.rulewerk.core.model.api.Literal;
import org.semanticweb.rulewerk.core.model.api.PositiveLiteral;
import org.semanticweb.rulewerk.core.model.api.Predicate;
import org.semanticweb.rulewerk.core.model.api.Rule;
import org.semanticweb.rulewerk.core.model.api.Statement;
import org.semanticweb.rulewerk.core.model.api.Term;
import org.semanticweb.rulewerk.core.model.implementation.Expressions;
import org.semanticweb.rulewerk.core.reasoner.AcyclicityNotion;
import org.semanticweb.rulewerk.core.reasoner.Algorithm;
import org.semanticweb.rulewerk.core.reasoner.Correctness;
import org.semanticweb.rulewerk.core.reasoner.CyclicityResult;
import org.semanticweb.rulewerk.core.reasoner.KnowledgeBase;
import org.semanticweb.rulewerk.core.reasoner.LogLevel;
import org.semanticweb.rulewerk.core.reasoner.QueryAnswerCount;
import org.semanticweb.rulewerk.core.reasoner.QueryResultIterator;
import org.semanticweb.rulewerk.core.reasoner.Reasoner;
import org.semanticweb.rulewerk.core.reasoner.ReasonerState;
import org.semanticweb.rulewerk.core.reasoner.RuleRewriteStrategy;
import org.semanticweb.rulewerk.core.reasoner.implementation.EmptyQueryResultIterator;
import org.semanticweb.rulewerk.core.reasoner.implementation.QueryAnswerCountImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import karmaresearch.vlog.AlreadyStartedException;
import karmaresearch.vlog.EDBConfigurationException;
import karmaresearch.vlog.MaterializationException;
import karmaresearch.vlog.NonExistingPredicateException;
import karmaresearch.vlog.NotStartedException;
import karmaresearch.vlog.TermQueryResultIterator;
import karmaresearch.vlog.VLog;
import karmaresearch.vlog.VLog.CyclicCheckResult;

/**
 * Reasoner implementation using the VLog backend.
 *
 *
 *
 * @author Markus Kroetzsch
 *
 */
public class VLogReasoner implements Reasoner {
	private static Logger LOGGER = LoggerFactory.getLogger(VLogReasoner.class);

	final KnowledgeBase knowledgeBase;
	final VLog vLog = new VLog();

	private ReasonerState reasonerState = ReasonerState.KB_NOT_LOADED;
	private Correctness correctness = Correctness.SOUND_BUT_INCOMPLETE;

	private LogLevel internalLogLevel = LogLevel.WARNING;
	private Algorithm algorithm = Algorithm.RESTRICTED_CHASE;
	private Integer timeoutAfterSeconds;
	private RuleRewriteStrategy ruleRewriteStrategy = RuleRewriteStrategy.NONE;

	/**
	 * Holds the state of the reasoning result. Has value {@code true} if reasoning
	 * has completed, {@code false} if it has been interrupted.
	 */
	private boolean reasoningCompleted;

	public VLogReasoner(final KnowledgeBase knowledgeBase) {
		super();
		this.knowledgeBase = knowledgeBase;
		this.knowledgeBase.addListener(this);

		this.setLogLevel(this.internalLogLevel);
	}

	@Override
	public KnowledgeBase getKnowledgeBase() {
		return this.knowledgeBase;
	}

	@Override
	public void setAlgorithm(final Algorithm algorithm) {
		Validate.notNull(algorithm, "Algorithm cannot be null!");
		this.validateNotClosed();
		this.algorithm = algorithm;
	}

	@Override
	public Algorithm getAlgorithm() {
		return this.algorithm;
	}

	@Override
	public void setReasoningTimeout(final Integer seconds) {
		this.validateNotClosed();
		if (seconds != null) {
			Validate.isTrue(seconds > 0, "Only strictly positive timeout period allowed!", seconds);
		}
		this.timeoutAfterSeconds = seconds;
	}

	@Override
	public Integer getReasoningTimeout() {
		return this.timeoutAfterSeconds;
	}

	@Override
	public void setRuleRewriteStrategy(final RuleRewriteStrategy ruleRewritingStrategy) {
		this.validateNotClosed();
		Validate.notNull(ruleRewritingStrategy, "Rewrite strategy cannot be null!");
		this.ruleRewriteStrategy = ruleRewritingStrategy;
	}

	@Override
	public RuleRewriteStrategy getRuleRewriteStrategy() {
		return this.ruleRewriteStrategy;
	}

	@Override
	public Correctness getCorrectness() {
		return this.correctness;
	}

	/*
	 * TODO Due to automatic predicate renaming, it can happen that an EDB predicate
	 * cannot be queried after loading unless reasoning has already been invoked
	 * (since the auxiliary rule that imports the EDB facts to the "real" predicate
	 * must be used). This issue could be weakened by rewriting queries to
	 * (single-source) EDB predicates internally when in such a state.
	 */
	void load() throws IOException {
		this.validateNotClosed();

		switch (this.reasonerState) {
		case KB_NOT_LOADED:
			this.loadKnowledgeBase();
			break;
		case KB_LOADED:
		case MATERIALISED:
			// do nothing, all KB is already loaded
			break;
		case KB_CHANGED:
			this.resetReasoner();
			this.loadKnowledgeBase();
		default:
			break;
		}
	}

	void loadKnowledgeBase() throws IOException {
		LOGGER.info("Started loading knowledge base ...");

		final VLogKnowledgeBase vLogKB = new VLogKnowledgeBase(this.knowledgeBase);

		if (!vLogKB.hasData()) {
			LOGGER.warn("No data statements (facts or datasource declarations) have been provided.");
		}

		// 1. vLog is initialized by loading VLog data sources
		this.loadVLogDataSources(vLogKB);

		// 2. in-memory data is loaded
		this.loadInMemoryDataSources(vLogKB);
		this.validateDataSourcePredicateArities(vLogKB);

		this.loadFacts(vLogKB);

		// 3. rules are loaded
		this.loadRules(vLogKB);

		this.reasonerState = ReasonerState.KB_LOADED;

		// if there are no rules, then materialisation state is complete
		this.correctness = !vLogKB.hasRules() ? Correctness.SOUND_AND_COMPLETE : Correctness.SOUND_BUT_INCOMPLETE;

		LOGGER.info("Finished loading knowledge base.");
	}

	void loadVLogDataSources(final VLogKnowledgeBase vLogKB) throws IOException {
		try {
			this.vLog.start(vLogKB.getVLogDataSourcesConfigurationString(), false);
		} catch (final AlreadyStartedException e) {
			throw new RulewerkRuntimeException("Inconsistent reasoner state.", e);
		} catch (final EDBConfigurationException e) {
			throw new RulewerkRuntimeException("Invalid data sources configuration.", e);
		}
	}

	void loadInMemoryDataSources(final VLogKnowledgeBase vLogKB) {
		vLogKB.getEdbPredicates().forEach((k, v) -> this.loadInMemoryDataSource(v.getDataSource(), k));

		vLogKB.getAliasesForEdbPredicates().forEach((k, v) -> this.loadInMemoryDataSource(k.getDataSource(), v));
	}

	void loadInMemoryDataSource(final DataSource dataSource, final Predicate predicate) {
		if (dataSource instanceof VLogInMemoryDataSource) {

			final VLogInMemoryDataSource inMemoryDataSource = (VLogInMemoryDataSource) dataSource;
			try {
				this.load(predicate, inMemoryDataSource);
			} catch (final EDBConfigurationException e) {
				throw new RulewerkRuntimeException("Invalid data sources configuration!", e);
			}
		}
	}

	void load(final Predicate predicate, final VLogInMemoryDataSource inMemoryDataSource)
			throws EDBConfigurationException {
		final String vLogPredicateName = ModelToVLogConverter.toVLogPredicate(predicate);

		this.vLog.addData(vLogPredicateName, inMemoryDataSource.getData());

		if (LOGGER.isDebugEnabled()) {
			for (final String[] tuple : inMemoryDataSource.getData()) {
				LOGGER.debug("Loaded direct fact {}{}.", vLogPredicateName, Arrays.toString(tuple));
			}
		}
	}

	/**
	 * Checks if the loaded external data sources do in fact contain data of the
	 * correct arity.
	 *
	 * @throws IncompatiblePredicateArityException to indicate a problem
	 *                                             (non-checked exception)
	 */
	void validateDataSourcePredicateArities(final VLogKnowledgeBase vLogKB) throws IncompatiblePredicateArityException {

		vLogKB.getEdbPredicates().forEach((k, v) -> this.validateDataSourcePredicateArity(k, v.getDataSource()));

		vLogKB.getAliasesForEdbPredicates()
				.forEach((k, v) -> this.validateDataSourcePredicateArity(v, k.getDataSource()));
	}

	/**
	 * Checks if the loaded external data for a given source does in fact contain
	 * data of the correct arity for the given predidate.
	 *
	 * @param predicate  the predicate for which data is loaded
	 * @param dataSource the data source used
	 *
	 * @throws IncompatiblePredicateArityException to indicate a problem
	 *                                             (non-checked exception)
	 */
	void validateDataSourcePredicateArity(final Predicate predicate, final DataSource dataSource)
			throws IncompatiblePredicateArityException {
		if (dataSource == null) {
			return;
		}
		try {
			final int dataSourcePredicateArity = this.vLog
					.getPredicateArity(ModelToVLogConverter.toVLogPredicate(predicate));
			if (dataSourcePredicateArity == -1) {
				LOGGER.warn("Data source {} for predicate {} is empty! ", dataSource, predicate);
			} else if (predicate.getArity() != dataSourcePredicateArity) {
				throw new IncompatiblePredicateArityException(predicate, dataSourcePredicateArity, dataSource);
			}
		} catch (final NotStartedException e) {
			throw new RulewerkRuntimeException("Inconsistent reasoner state!", e);
		}
	}

	void loadFacts(final VLogKnowledgeBase vLogKB) {
		final Map> directEdbFacts = vLogKB.getDirectEdbFacts();

		directEdbFacts.forEach((k, v) -> {
			try {
				final String vLogPredicateName = ModelToVLogConverter.toVLogPredicate(vLogKB.getAlias(k));
				final String[][] vLogPredicateTuples = ModelToVLogConverter.toVLogFactTuples(v);

				this.vLog.addData(vLogPredicateName, vLogPredicateTuples);

				if (LOGGER.isDebugEnabled()) {
					for (final String[] tuple : vLogPredicateTuples) {
						LOGGER.debug("Loaded direct fact {}{}.", vLogPredicateName, Arrays.toString(tuple));
					}
				}
			} catch (final EDBConfigurationException e) {
				throw new RulewerkRuntimeException("Invalid data sources configuration!", e);
			}

		});
	}

	void loadRules(final VLogKnowledgeBase vLogKB) {
		final karmaresearch.vlog.Rule[] vLogRuleArray = ModelToVLogConverter.toVLogRuleArray(vLogKB.getRules());
		final karmaresearch.vlog.VLog.RuleRewriteStrategy vLogRuleRewriteStrategy = ModelToVLogConverter
				.toVLogRuleRewriteStrategy(this.ruleRewriteStrategy);
		try {
			this.vLog.setRules(vLogRuleArray, vLogRuleRewriteStrategy);
			if (LOGGER.isDebugEnabled()) {
				for (final karmaresearch.vlog.Rule rule : vLogRuleArray) {
					LOGGER.debug("Loaded rule {}.", rule.toString());
				}
			}
		} catch (final NotStartedException e) {
			throw new RulewerkRuntimeException("Inconsistent reasoner state!", e);
		}
	}

	@Override
	public boolean reason() throws IOException {
		this.validateNotClosed();

		switch (this.reasonerState) {
		case KB_NOT_LOADED:
			this.load();
			this.runChase();
			break;
		case KB_LOADED:
			this.runChase();
			break;
		case KB_CHANGED:
			this.resetReasoner();
			this.load();
			this.runChase();
			break;
		case MATERIALISED:
			this.runChase();
			break;
		default:
			break;
		}

		return this.reasoningCompleted;
	}

	private void runChase() {
		LOGGER.info("Started materialisation of inferences ...");
		this.reasonerState = ReasonerState.MATERIALISED;

		final boolean skolemChase = this.algorithm == Algorithm.SKOLEM_CHASE;
		try {
			if (this.timeoutAfterSeconds == null) {
				this.vLog.materialize(skolemChase);
				this.reasoningCompleted = true;
			} else {
				this.reasoningCompleted = this.vLog.materialize(skolemChase, this.timeoutAfterSeconds);
			}
		} catch (final NotStartedException e) {
			throw new RulewerkRuntimeException("Inconsistent reasoner state.", e);
		} catch (final MaterializationException e) {
			throw new RulewerkRuntimeException("VLog encounterd an error during materialization: " + e.getMessage(), e);
		}

		if (this.reasoningCompleted) {
			this.correctness = Correctness.SOUND_AND_COMPLETE;
			LOGGER.info("Completed materialisation of inferences.");
		} else {
			this.correctness = Correctness.SOUND_BUT_INCOMPLETE;
			LOGGER.info("Stopped materialisation of inferences (possibly incomplete).");
		}
	}

	@Override
	public QueryResultIterator answerQuery(final PositiveLiteral query, final boolean includeNulls) {
		this.validateBeforeQuerying(query);

		final boolean filterBlanks = !includeNulls;
		final karmaresearch.vlog.Atom vLogAtom = ModelToVLogConverter.toVLogAtom(query);

		final karmaresearch.vlog.QueryResultIterator queryResultIterator;

		try {
			final int predicateId = this.vLog.getPredicateId(vLogAtom.getPredicate());
			final long[] terms = this.extractTerms(vLogAtom.getTerms());
			queryResultIterator = this.vLog.query(predicateId, terms, true, filterBlanks);
		} catch (final NotStartedException e) {
			throw new RulewerkRuntimeException("Inconsistent reasoner state.", e);
		} catch (final NonExistingPredicateException e1) {
			return this.createEmptyResultIterator(query);
		}

		this.logWarningOnCorrectness(this.correctness);
		return new VLogFastQueryResultIterator(queryResultIterator, this.correctness, this.vLog);
	}

	private QueryResultIterator createEmptyResultIterator(final PositiveLiteral query) {
		final Correctness answerCorrectness = this.getCorrectnessUnknownPredicate(query);
		this.logWarningOnCorrectness(answerCorrectness);
		return new EmptyQueryResultIterator(answerCorrectness);
	}

	private Correctness getCorrectnessUnknownPredicate(final PositiveLiteral query) {
		final Correctness answerCorrectness;
		if (this.reasonerState == ReasonerState.MATERIALISED) {
			this.warnUnknownPredicate(query);
			answerCorrectness = Correctness.SOUND_AND_COMPLETE;
		} else {
			answerCorrectness = Correctness.SOUND_BUT_INCOMPLETE;
		}
		return answerCorrectness;
	}

	private void warnUnknownPredicate(final PositiveLiteral query) {
		LOGGER.warn("Query uses predicate " + query.getPredicate()
				+ " that does not occur in the materialised knowledge base. Answer must be empty!");
	}

	/**
	 * Utility method copied from {@link karmaresearch.vlog.VLog}.
	 * 
	 * @FIXME This should be provided by VLog and made visible to us rather than
	 *        being copied here.
	 * @param terms
	 * @return
	 * @throws NotStartedException
	 */
	private long[] extractTerms(final karmaresearch.vlog.Term[] terms) throws NotStartedException {
		final ArrayList variables = new ArrayList<>();
		final long[] longTerms = new long[terms.length];
		for (int i = 0; i < terms.length; i++) {
			if (terms[i].getTermType() == karmaresearch.vlog.Term.TermType.VARIABLE) {
				boolean found = false;
				for (int j = 0; j < variables.size(); j++) {
					if (variables.get(j).equals(terms[i].getName())) {
						found = true;
						longTerms[i] = -j - 1;
						break;
					}
				}
				if (!found) {
					variables.add(terms[i].getName());
					longTerms[i] = -variables.size();
				}
			} else {
				longTerms[i] = this.vLog.getOrAddConstantId(terms[i].getName());
			}
		}
		return longTerms;
	}

	@Override
	public QueryAnswerCount countQueryAnswers(final PositiveLiteral query, final boolean includeNulls) {
		this.validateBeforeQuerying(query);

		final boolean filterBlanks = !includeNulls;
		final karmaresearch.vlog.Atom vLogAtom = ModelToVLogConverter.toVLogAtom(query);

		long result;
		try {
			result = this.vLog.querySize(vLogAtom, true, filterBlanks);
		} catch (final NotStartedException e) {
			throw new RulewerkRuntimeException("Inconsistent reasoner state.", e);
		} catch (final NonExistingPredicateException e) {
			return this.createEmptyResultCount(query);
		}
		this.logWarningOnCorrectness(this.correctness);
		return new QueryAnswerCountImpl(this.correctness, result);
	}

	private QueryAnswerCount createEmptyResultCount(final PositiveLiteral query) {
		final Correctness correctness = this.getCorrectnessUnknownPredicate(query);
		this.logWarningOnCorrectness(correctness);
		return new QueryAnswerCountImpl(correctness, 0);
	}

	@Override
	public Correctness exportQueryAnswersToCsv(final PositiveLiteral query, final String csvFilePath,
			final boolean includeBlanks) throws IOException {
		this.validateBeforeQuerying(query);

		Validate.notNull(csvFilePath, "File to export query answer to must not be null!");
		Validate.isTrue(csvFilePath.endsWith(".csv"), "Expected .csv extension for file [%s]!", csvFilePath);

		final boolean filterBlanks = !includeBlanks;
		final karmaresearch.vlog.Atom vLogAtom = ModelToVLogConverter.toVLogAtom(query);
		try {
			this.vLog.writeQueryResultsToCsv(vLogAtom, csvFilePath, filterBlanks);
		} catch (final NotStartedException e) {
			throw new RulewerkRuntimeException("Inconsistent reasoner state!", e);
		} catch (final NonExistingPredicateException e1) {
			final Correctness correctness = this.getCorrectnessUnknownPredicate(query);
			this.logWarningOnCorrectness(correctness);
			return correctness;
		}
		this.logWarningOnCorrectness(this.correctness);
		return this.correctness;
	}

	private void validateBeforeQuerying(final PositiveLiteral query) {
		this.validateNotClosed();
		if (this.reasonerState == ReasonerState.KB_NOT_LOADED) {
			throw new ReasonerStateException(this.reasonerState,
					"Querying is not allowed before Reasoner#reason() was first called!");
		}
		Validate.notNull(query, "Query atom must not be null!");
	}

	@Override
	public Correctness forEachInference(final InferenceAction action) throws IOException {
		this.validateNotClosed();
		if (this.reasonerState == ReasonerState.KB_NOT_LOADED) {
			throw new ReasonerStateException(this.reasonerState,
					"Obtaining inferences is not alowed before reasoner is loaded!");
		}
		final Set toBeQueriedHeadPredicates = this.getKnowledgeBasePredicates();

		for (final Predicate predicate : toBeQueriedHeadPredicates) {
			final PositiveLiteral queryAtom = this.getQueryAtom(predicate);
			final karmaresearch.vlog.Atom vLogAtom = ModelToVLogConverter.toVLogAtom(queryAtom);
			try (final TermQueryResultIterator answers = this.vLog.query(vLogAtom, true, false)) {
				while (answers.hasNext()) {
					final karmaresearch.vlog.Term[] vlogTerms = answers.next();
					final List termList = VLogToModelConverter.toTermList(vlogTerms);
					action.accept(predicate, termList);
				}
			} catch (final NotStartedException e) {
				throw new RulewerkRuntimeException("Inconsistent reasoner state.", e);
			} catch (final NonExistingPredicateException e1) {
				throw new RulewerkRuntimeException("Inconsistent knowledge base state.", e1);
			}
		}

		this.logWarningOnCorrectness(this.correctness);
		return this.correctness;
	}

	private void logWarningOnCorrectness(final Correctness correctness) {
		if (correctness != Correctness.SOUND_AND_COMPLETE) {
			LOGGER.warn("Query answers may be {} with respect to the current Knowledge Base!", this.correctness);
		}
	}

	@Override
	public void resetReasoner() {
		this.validateNotClosed();
		this.reasonerState = ReasonerState.KB_NOT_LOADED;
		this.vLog.stop();
		LOGGER.info("Reasoner has been reset. All inferences computed during reasoning have been discarded.");
	}

	@Override
	public void close() {
		if (this.reasonerState == ReasonerState.CLOSED) {
			LOGGER.info("Reasoner is already closed.");
		} else {
			this.reasonerState = ReasonerState.CLOSED;
			this.knowledgeBase.deleteListener(this);
			this.vLog.stop();
			LOGGER.info("Reasoner closed.");
		}
	}

	@Override
	public void setLogLevel(final LogLevel logLevel) {
		this.validateNotClosed();
		Validate.notNull(logLevel, "Log level cannot be null!");
		this.internalLogLevel = logLevel;
		this.vLog.setLogLevel(ModelToVLogConverter.toVLogLogLevel(this.internalLogLevel));
	}

	@Override
	public LogLevel getLogLevel() {
		return this.internalLogLevel;
	}

	@Override
	public void setLogFile(final String filePath) {
		this.validateNotClosed();
		this.vLog.setLogFile(filePath);
	}

	@Override
	public boolean isJA() {
		return this.checkAcyclicity(AcyclicityNotion.JA);
	}

	@Override
	public boolean isRJA() {
		return this.checkAcyclicity(AcyclicityNotion.RJA);
	}

	@Override
	public boolean isMFA() {
		return this.checkAcyclicity(AcyclicityNotion.MFA);
	}

	@Override
	public boolean isRMFA() {
		return this.checkAcyclicity(AcyclicityNotion.RMFA);
	}

	@Override
	public boolean isMFC() {
		this.validateNotClosed();
		if (this.reasonerState == ReasonerState.KB_NOT_LOADED) {
			try {
				this.load();
			} catch (final IOException e) { // FIXME: quick fix for https://github.com/knowsys/rulewerk/issues/128
				throw new RulewerkRuntimeException(e);
			}
		}

		CyclicCheckResult checkCyclic;
		try {
			checkCyclic = this.vLog.checkCyclic("MFC");
		} catch (final NotStartedException e) {
			throw new RulewerkRuntimeException(e.getMessage(), e); // should be impossible
		}
		return checkCyclic.equals(CyclicCheckResult.CYCLIC);
	}

	@Override
	public CyclicityResult checkForCycles() {
		final boolean acyclic = this.isJA() || this.isRJA() || this.isMFA() || this.isRMFA();
		if (acyclic) {
			return CyclicityResult.ACYCLIC;
		} else {
			final boolean cyclic = this.isMFC();
			if (cyclic) {
				return CyclicityResult.CYCLIC;
			}
			return CyclicityResult.UNDETERMINED;
		}
	}

	@Override
	public void onStatementsAdded(final List statementsAdded) {
		// TODO more elaborate materialisation state handling

		this.updateReasonerToKnowledgeBaseChanged();

		// updateCorrectnessOnStatementsAdded(statementsAdded);
		this.updateCorrectnessOnStatementsAdded();
	}

	@Override
	public void onStatementAdded(final Statement statementAdded) {
		// TODO more elaborate materialisation state handling

		this.updateReasonerToKnowledgeBaseChanged();

		// updateCorrectnessOnStatementAdded(statementAdded);
		this.updateCorrectnessOnStatementsAdded();
	}

	@Override
	public void onStatementRemoved(final Statement statementRemoved) {
		this.updateReasonerToKnowledgeBaseChanged();
		this.updateCorrectnessOnStatementsRemoved();
	}

	@Override
	public void onStatementsRemoved(final List statementsRemoved) {
		this.updateReasonerToKnowledgeBaseChanged();
		this.updateCorrectnessOnStatementsRemoved();
	}

	Set getKnowledgeBasePredicates() {
		final Set toBeQueriedHeadPredicates = new HashSet<>();
		for (final Rule rule : this.knowledgeBase.getRules()) {
			for (final Literal literal : rule.getHead()) {
				toBeQueriedHeadPredicates.add(literal.getPredicate());
			}
		}
		for (final DataSourceDeclaration dataSourceDeclaration : this.knowledgeBase.getDataSourceDeclarations()) {
			toBeQueriedHeadPredicates.add(dataSourceDeclaration.getPredicate());
		}
		for (final Fact fact : this.knowledgeBase.getFacts()) {
			toBeQueriedHeadPredicates.add(fact.getPredicate());
		}
		return toBeQueriedHeadPredicates;
	}

	private PositiveLiteral getQueryAtom(final Predicate predicate) {
		final List toBeGroundedVariables = new ArrayList<>(predicate.getArity());
		for (int i = 0; i < predicate.getArity(); i++) {
			toBeGroundedVariables.add(Expressions.makeUniversalVariable("X" + i));
		}
		return Expressions.makePositiveLiteral(predicate, toBeGroundedVariables);
	}

	private boolean checkAcyclicity(final AcyclicityNotion acyclNotion) {
		this.validateNotClosed();
		if (this.reasonerState == ReasonerState.KB_NOT_LOADED) {
			try {
				this.load();
			} catch (final IOException e) { // FIXME: quick fix for https://github.com/knowsys/rulewerk/issues/128
				throw new RulewerkRuntimeException(e);
			}
		}

		CyclicCheckResult checkCyclic;
		try {
			checkCyclic = this.vLog.checkCyclic(acyclNotion.name());
		} catch (final NotStartedException e) {
			throw new RulewerkRuntimeException(e.getMessage(), e); // should be impossible
		}
		return checkCyclic.equals(CyclicCheckResult.NON_CYCLIC);
	}

	private void updateReasonerToKnowledgeBaseChanged() {
		if (this.reasonerState.equals(ReasonerState.KB_LOADED)
				|| this.reasonerState.equals(ReasonerState.MATERIALISED)) {

			this.reasonerState = ReasonerState.KB_CHANGED;
		}
	}

	private void updateCorrectnessOnStatementsAdded() {
		if (this.reasonerState == ReasonerState.KB_CHANGED) {
			// TODO refine
			this.correctness = Correctness.INCORRECT;
		}
	}

	private void updateCorrectnessOnStatementsRemoved() {
		if (this.reasonerState == ReasonerState.KB_CHANGED) {
			// TODO refine
			this.correctness = Correctness.INCORRECT;
		}
	}

	/**
	 * Check if reasoner is closed and throw an exception if it is.
	 *
	 * @throws ReasonerStateException
	 */
	void validateNotClosed() throws ReasonerStateException {
		if (this.reasonerState == ReasonerState.CLOSED) {
			LOGGER.error("Invalid operation requested on a closed reasoner object!");
			throw new ReasonerStateException(this.reasonerState, "Operation not allowed after closing reasoner!");
		}
	}

	ReasonerState getReasonerState() {
		return this.reasonerState;
	}

	void setReasonerState(final ReasonerState reasonerState) {
		this.reasonerState = reasonerState;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy