com.cyc.kb.client.AssertionImpl Maven / Gradle / Ivy
Show all versions of cyc-kb-client Show documentation
package com.cyc.kb.client;
/*
* #%L
* File: AssertionImpl.java
* Project: KB Client
* %%
* Copyright (C) 2013 - 2017 Cycorp, Inc
* %%
* 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 com.cyc.base.CycAccess;
import com.cyc.base.cycobject.CycAssertion;
import com.cyc.base.cycobject.CycList;
import com.cyc.base.cycobject.CycObject;
import com.cyc.base.cycobject.CycSymbol;
import com.cyc.base.cycobject.ElMt;
import com.cyc.base.cycobject.FormulaSentence;
import com.cyc.base.exception.CycApiException;
import com.cyc.base.exception.CycConnectionException;
import com.cyc.baseclient.CycObjectFactory;
import com.cyc.baseclient.cycobject.CycSymbolImpl;
import com.cyc.baseclient.cycobject.DefaultCycObjectImpl;
import com.cyc.baseclient.cycobject.FormulaSentenceImpl;
import com.cyc.baseclient.inference.params.DefaultInferenceParameters;
import com.cyc.baseclient.inference.params.OpenCycInferenceParameterEnum;
import com.cyc.kb.Assertion;
import com.cyc.kb.Assertion.Direction;
import com.cyc.kb.Assertion.Strength;
import com.cyc.kb.Context;
import com.cyc.kb.KbCollection;
import com.cyc.kb.KbObject;
import com.cyc.kb.Sentence;
import static com.cyc.kb.client.KbContentLogger.KB_FIND_LOGGER;
import static com.cyc.kb.client.KbObjectImpl.getStaticAccess;
import com.cyc.kb.client.config.KbConfiguration;
import com.cyc.kb.exception.CreateException;
import com.cyc.kb.exception.DeleteException;
import com.cyc.kb.exception.InvalidFormulaInContextException;
import com.cyc.kb.exception.KbException;
import com.cyc.kb.exception.KbObjectNotFoundException;
import com.cyc.kb.exception.KbRuntimeException;
import com.cyc.kb.exception.KbTypeException;
import com.cyc.query.parameters.InferenceParameters;
import java.util.ArrayList;
import java.util.Collection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An Assertion object is a facade for a
* #$CycLAssertion
in Cyc KB. An assertion is a semantically well
* formed Sentence, in a specific Context.
*
* Sub-classes include Fact and Rule.
*
* @author Vijay Raj
* @version $Id: AssertionImpl.java 173082 2017-07-28 15:36:55Z nwinant $
* @since 1.0
*/
public class AssertionImpl extends PossiblyNonAtomicKbObjectImpl implements Assertion {
/**
* In some cases, the act of asserting a formula may be successful without actually
* creating an assertion in the KB. For example, a #$SKSIContentMicrotheory
* provides a "window" into an external knowledge source, and any knowledge asserted into a
* #$SKSIContentMicrotheory will be stored in that external knowledge source. In other words, an
* assertion into a particular Context may cause rows to be added to, e.g., a relational database
* where Cyc may have retrieve and incorporate them into query answers, but without any actual
* assertion object being created in Cyc's KB. In such cases -- where the asserted sentence has
* been successfully stored, but there is no Assertion object to return -- #get() and
* #findOrCreate() should return null
instead of throwing an exception.
*/
private static final boolean TREAT_EXTERNAL_SKS_KNOWLEDGE_AS_ASSERTIONS = true;
private static final Logger LOG = LoggerFactory.getLogger(AssertionImpl.class.getCanonicalName());
private static final KbContentLogger KB_LOG = KbContentLogger.getInstance();
public static boolean VERBOSE_ASSERT_ERRORS_DEFAULT = true;
/**
* This not part of the public, supported KB API. default constructor, calls the default
* super constructor
*
* @throws KbRuntimeException if there is a problem connecting to Cyc
*/
AssertionImpl() {
super();
}
/**
* This not part of the public, supported KB API. an implementation-dependent constructor
*
* Return a new
* Assertion
based on the existing CycAssertion object
* cycAssert
. The KB assertion underlying
* cycAssert
must already be a #$CycLAssertion.
*
* It is used when the result of query is a CycObject and is known to be or
* requested to be cast as an Assertion.
*
* @param cycAssert the CycObject wrapped by Assertion. The constructor
* verifies that the CycObject is an #$CycLAssertion.
*
* @throws KbTypeException if cycAssert (which already exists) is not
* a #$CycLAssertion
*/
// We have made this public for the reflection mechanism to see this class.
// If made package private, reflection of getConstructor(CycObject.class) fails
@Deprecated
AssertionImpl(CycAssertion cycAssert) throws KbTypeException {
super(cycAssert);
}
/**
* Get the Assertion
object that corresponds to
* cycAssert
. Throws exceptions if the object isn't in the KB, or if
* it's not already an assertion. (Currently cycAssert is only checked to be an
* instance of CycAssertion. )
*
* @param cycAssert candidate assertion object
*
* @return an Assertion based on cycAssert
*
* @throws KbTypeException if the cycAssert is not an instance of assertion
* @throws CreateException
*/
@Deprecated
public static Assertion get(CycObject cycAssert) throws KbTypeException, CreateException {
return KbObjectImplFactory.get(cycAssert, AssertionImpl.class);
}
@SuppressWarnings("deprecation")
public static Assertion get(String hlid) throws KbTypeException, CreateException {
// NOTE: The StandardKBObject was too geared towards Term (Constant, NAT) creation
// Did not want to overload that with assertion creation as well.
// Also the get method here takes only hlid. For a factory method that takes String to
// find an assertion, see get(String formulaStr, String ctxStr)
final Object result;
try {
result = DefaultCycObjectImpl.fromPossibleCompactExternalId(hlid, getStaticAccess());
} catch (CycConnectionException e){
throw new KbRuntimeException(e.getMessage(), e);
}
if (result instanceof CycAssertion) {
LOG.debug("Found assertion: {} using HLID: {}", result, hlid);
return KbObjectImplFactory.get((CycObject)result, AssertionImpl.class);
} else {
String msg = "Could not find any Assertion with hlid: " + hlid + " in the KB.";
LOG.error(msg);
throw new KbObjectNotFoundException(msg);
}
}
@SuppressWarnings("deprecation")
public static Assertion get(Sentence formula, Context ctx)
throws KbTypeException, CreateException {
//@todo: There are two purposes of caching
// 1. Reduce round trip to KB
// 2. Use the same object if core is equal.
// Since the cache key is cyclify() and hlid, we have to find the assertion
// using formulaStr and ctxStr to get any of the cache keys. Which means we have to
// do one trip to the KB anyways. But we still use KBObjectFactory.get to reuse the
// same KBObject.
// A separate KBObjectFactory method that takes the ist sentence of formula and mt,
// could also eliminate the lookup step.
final CycAssertion result
= findAssertion(FormulaSentence.class.cast(formula.getCore()), ContextImpl.asELMt(ctx));
return convertToFoundAssertion(result, formula, ctx, AssertionImpl.class);
}
@SuppressWarnings("deprecation")
public static Assertion get(String formulaStr, String ctxStr)
throws KbTypeException, CreateException {
final CycAssertion result = findAssertion(formulaStr, ctxStr);
return convertToFoundAssertion(result, formulaStr, ctxStr, AssertionImpl.class);
}
protected static O throwAssertException(
Sentence formula, Context ctx, Exception ex, boolean verbose) {
if (ex instanceof KbRuntimeException) {
throw (KbRuntimeException) ex;
}
final String msg = "Could not assert " + formula + " in " + ctx;
if (verbose) {
final String explanations = formula.notAssertibleExplanation(ctx);
throw new KbRuntimeException(msg + ":\n" + explanations, ex);
} else {
throw new KbRuntimeException(msg, ex);
}
}
@SuppressWarnings("deprecation")
public static Assertion findOrCreate(String formulaStr, String ctxStr, Strength s, Direction d)
throws KbTypeException, CreateException {
// @todo: There are two purposes of caching
// 1. Reduce round trip to KB
// 2. Use the same object if core is equal.
// Since the cache key is cyclify() and hlid, we have to find the assertion
// using formulaStr and ctxStr to get any of the cache keys. Which means we have to
// do one trip to the KB anyways. But we still use KBObjectFactory.get to reuse the
// same KBObject.
// A separate KBObjectFactory method that takes the ist sentence of formula and mt,
// could also eliminate the lookup step.
// The assertSentence tries to find the assertion anyways, before actually trying
// to assert.
final CycAssertion result = assertSentence(formulaStr, ctxStr, s, d);
return convertToFoundOrCreatedAssertion(result, formulaStr, ctxStr, AssertionImpl.class);
}
public static Assertion findOrCreate(String formulaStr, String ctxStr)
throws KbTypeException, CreateException {
return findOrCreate(formulaStr, ctxStr, Strength.AUTO, Direction.AUTO);
}
public static Assertion findOrCreate(String formulaStr) throws KbTypeException, CreateException {
return findOrCreate(formulaStr, KbConfiguration.getDefaultContext().forAssertion().toString());
}
@SuppressWarnings("deprecation")
public static Assertion findOrCreate(
Sentence formula, Context ctx, Strength s, Direction d, boolean verbose)
throws KbTypeException, CreateException {
try {
final CycAssertion result
= assertSentence(FormulaSentence.class.cast(formula.getCore()), ctx, s, d);
return convertToFoundOrCreatedAssertion(result, formula, ctx, AssertionImpl.class);
} catch (CycApiException ex) {
return throwAssertException(formula, ctx, ex, verbose);
}
}
@SuppressWarnings("deprecation")
public static Assertion findOrCreate(Sentence formula, Context ctx, Strength s, Direction d)
throws KbTypeException, CreateException {
return findOrCreate(formula, ctx, s, d, VERBOSE_ASSERT_ERRORS_DEFAULT);
}
public static Assertion findOrCreate(Sentence formula, Context ctx, boolean verbose)
throws KbTypeException, CreateException {
return findOrCreate(formula, ctx, Strength.AUTO, Direction.AUTO, verbose);
}
public static Assertion findOrCreate(Sentence formula, Context ctx)
throws KbTypeException, CreateException {
return findOrCreate(formula, ctx, VERBOSE_ASSERT_ERRORS_DEFAULT);
}
public static Assertion findOrCreate(Sentence formula, boolean verbose)
throws KbTypeException, CreateException {
return findOrCreate(formula, KbConfiguration.getDefaultContext().forAssertion(), verbose);
}
public static Assertion findOrCreate(Sentence formula) throws KbTypeException, CreateException {
return findOrCreate(formula, VERBOSE_ASSERT_ERRORS_DEFAULT);
}
@Override
public Sentence getFormula() {
try {
final FormulaSentence assertionFormula = getCore().getELFormula(getAccess());
return new SentenceImpl(assertionFormula);
} catch (CycApiException | CycConnectionException | KbTypeException | CreateException ex) {
LOG.error(ex.getMessage());
throw new KbRuntimeException(ex.getMessage(), ex);
}
}
@Override
@SuppressWarnings("deprecation")
public Context getContext() {
final CycAssertion ca = getCore();
final ContextImpl ctx;
try {
ctx = ContextImpl.get(ca.getMt());
} catch (CreateException | KbTypeException te) {
// The assertion is already created, that means the context should
// already be there.
throw new KbRuntimeException(te.getMessage(), te);
}
return ctx;
}
@Override
public Collection getSupportingAssertions() throws KbTypeException, CreateException {
try {
//String command = SublConstants.getInstance().assertionAssertedAssertionSupports.buildCommand(this.getCore());
//CycList> result = getAccess().converse().converseList(command);
final CycList> result = SublConstants.getInstance()
.assertionAssertedAssertionSupports.eval(getAccess(), this.getCore());
final Collection asserts = new ArrayList<>();
for (Object o : result) {
asserts.add(AssertionImpl.get((CycObject) o));
}
return asserts;
} catch (CycConnectionException e) {
throw new KbRuntimeException(e);
}
}
/* *
*
* @return the collection of all assertions that directly or indirectly
* support this assertion. Effectively this is a transitive version of
* {@link getSupportingAssertions}.
*/
/* this needs to be completed, and it should be basically the same as getSupportingAssertions with a high depth limit.
public Collection getAllSupportingAssertions () {
throw new UnsupportedOperationException();
}
*/
@Override
public Boolean isDeducedAssertion() {
try {
final String command
= SublConstants.getInstance().deducedAssertionQ.buildCommand(getCore());
return getAccess().converse().converseBoolean(command);
} catch (CycConnectionException e) {
throw new KbRuntimeException(e);
}
}
@Override
public Boolean isGroundAtomicFormula() {
return ((CycAssertion) this.getCore()).isGaf();
}
@Override
public Boolean isAssertedAssertion() {
try {
final String command
= SublConstants.getInstance().assertedAssertionQ.buildCommand(getCore());
return getAccess().converse().converseBoolean(command);
} catch (CycConnectionException e) {
throw new KbRuntimeException(e);
}
}
@Override
public Direction getDirection() {
try {
final String command
= SublConstants.getInstance().assertionDirection.buildCommand(getCore());
final CycObject co = getAccess().converse().converseCycObject(command);
if (co instanceof CycSymbol) {
final CycSymbol cs = (CycSymbol) co;
if (cs.equals(new CycSymbolImpl(":BACKWARD"))) {
return Direction.BACKWARD;
} else if (cs.equals(new CycSymbolImpl(":FORWARD"))) {
return Direction.FORWARD;
} else {
// This should never happen for CycAssertion, so a runtime exception
throw new KbRuntimeException("Unknown or :CODE Direction");
}
} else {
throw new KbRuntimeException("Unknown Direction");
}
} catch (CycConnectionException e) {
throw new KbRuntimeException(e.getMessage(), e);
}
}
@Override
public Assertion changeDirection(Direction d) throws KbException {
try {
if (this.getDirection().equals(d)) {
LOG.info(
"The input direction {} is the same as the assertion direction. Nothing to do.", d);
return this;
}
if (KbConfiguration.getCurrentCyclist() == null) {
throw new KbException("Set the Cyclist using KBAPIConfiguration.setCurrentCyclist()");
}
final String command = "(clet "
+ "((*the-cyclist* " + KbConfiguration.getCurrentCyclist().stringApiValue() + ")) "
+ "(" + SublConstants.getInstance().keChangeAssertionDirectionNow.stringApiValue()
+ " " + this.getCore().stringApiValue() + " :" + d.name() + "))";
final CycObject co = getAccess().converse().converseCycObject(command);
if (co instanceof CycAssertion) {
LOG.debug("Changed the assertion direction of " + this + " to: " + d);
// I'm guessing we were trying to force a ClassCastException here: - nwinant, 2017-05-04
//final CycAssertion ca = (CycAssertion) co;
return this;
} else {
throw new KbRuntimeException("Failed to change the direction of the assertion: " + this);
}
} catch (CycConnectionException e) {
throw new KbRuntimeException(e.getMessage(), e);
}
}
/**
* Retrigger forward inference against this assertion. In general when adding or removing
* assertions, Cyc's truth-maintenance system automatically handles this, but there are
* exceptional cases (e.g. where resource constraints prevented forward inference from running
* exhaustively) where it needs to be done manually. This is also known as repropagating the
* assertion.
*
* @return the assertion that had its forward inference retriggered.
* @throws KbException
*/
public Assertion retriggerForwardInference() throws KbException {
try {
if (KbConfiguration.getCurrentCyclist() == null) {
throw new KbException("Set the Cyclist using KBAPIConfiguration.setCurrentCyclist()");
}
final String command = "(clet "
+ "((*the-cyclist* " + KbConfiguration.getCurrentCyclist().stringApiValue() + ")) "
+ "(" + SublConstants.getInstance().keRepropagateAssertionNow.stringApiValue()
+ " " + this.getCore().stringApiValue() + "))";
final CycObject co = getAccess().converse().converseCycObject(command);
if (co instanceof CycAssertion) {
LOG.debug("Forward inference retriggered against " + this);
// I'm guessing we were trying to force a ClassCastException here: - nwinant, 2017-05-04
//final CycAssertion ca = (CycAssertion) co;
return this;
} else {
throw new KbRuntimeException("Failed to retrigger forward inference against " + this);
}
} catch (CycConnectionException e) {
throw new KbRuntimeException(e.getMessage(), e);
}
}
/**
* This not part of the public, supported KB API. Check that the candidate core
* object is valid CycAssertion. In the CycKB the object would be
* valid #$CycLAssertion
*
* @return true if the core is valid for a given class of KBObject
* @see StandardKBObject#isValidCore(CycObject) for more comments
*/
@Override
protected boolean isValidCore(CycObject cycObject) {
return cycObject instanceof CycAssertion;
}
/**
* Return the KBCollection as a KBObject of the Cyc term that
* underlies this class.
*
* @return KBCollectionImpl.get("#$CycLAssertion");
*/
@Override
public KbObject getType() {
return getClassType();
}
/**
* Return the KBCollection as a KBObject of the Cyc term that
* underlies this class.
*
* @return KBCollectionImpl.get("#$CycLAssertion");
*/
public static KbObject getClassType() {
try {
return KbCollectionImpl.get(getClassTypeString());
} catch (KbException kae) {
throw new KbRuntimeException(kae.getMessage(), kae);
}
}
@Override
String getTypeString() {
return getClassTypeString();
}
static String getClassTypeString() {
return "#$CycLAssertion";
}
//TODO preserve this and make it work
/*
* private CycAssertion findAssertion(Context ctx, Object...argList) throws Exception {
* List