Previous topic  Top  Next topic  Print this Topic
 

The IsNumber Example Built-in

 

All built-ins have to implement one of the following interfaces: IFilterBuiltin, IFunctionalBuiltin, IRelationalBuiltin and IConnector. These are different subclasses of built-ins. The filter built-ins just accept ground input tuples and return either true – if it matches the implemented filter, or false otherwise. The functional built-ins may return none or one single result, the relational built-ins may return none, one or several results. The connector built-ins are used to get data from external sources. A relational built-in implies a functional built-in, and a functional built-in implies a filter built-in. So a relational (or functional) built-in could be used instead of the other types, but this will decrease the performance.

All built-ins have to implement methods to give information about the built-in, to say when it can be evaluated and to do the evaluation. The advantage of using of interfaces is that you now don't have to subclass a fixed class. The constructor must not take parameters, and the method getInfo() has to deliver the corresponding BuiltinSpec at any time, after an instance of the built-in was constructed.

It is a good style of programming to let all built-ins (perhaps except connectors) accept tuples with all arguments ground. Please consider this example:

?- 2.0[_add(2.0)->?X] AND _square(?X,4.0).

To use the same functionality without multiply accepting ground tuples you would have to do this:

?- 2.0[_add(2.0)->?X] AND _square("Y",4.0) AND _unify(?X,"Y").

In this case this is just one additional built-in, but it could decrease the performance, as in this case the results for Y are 2 and -2, two times the work for unify. Consider a built-in that would create more than 1000 results. Let's first have a look at a built-in that is already available. The unary built-in <IsNumber>; decides whether its argument is a number or not. It is based on <IFilterBuiltin>.

package com.ontoprise.builtin.filterbuiltin;

import java.util.BitSet;

import org.apache.log4j.Logger;

import org.semanticweb.kaon2.api.KAON2Exception;

import org.semanticweb.kaon2.api.OntologyLanguage;

import org.semanticweb.kaon2.api.logic.Term;

import com.ontoprise.builtin.BuiltinContext;

import com.ontoprise.builtin.BuiltinSpec;

import com.ontoprise.builtin.builtin.BuiltinServices;

import com.ontoprise.builtin.interfaces.IFilterBuiltin;

import com.ontoprise.builtin.interfaces.IGrounds;

import com.ontoprise.util.OntopriseConstants;

/**

* Example builtin which checks if a given tuple is a number.

*

*/

public class IsNumberDemoBuiltin implements IFilterBuiltin {

  /**

    * Provide a no-argument constructor which does nothing - in this case.

    */

  public IsNumberDemoBuiltin() { // Empty as nothing is to do for this built-in

  }

  /**

    * This method is called during the evaluation. We must check for each input if the condition ("isNumber") matches.

    *

    * @param input Term[] of always ground values.

    * @return boolean if this input is true or not.

    * @throws KAON2Exception Is thrown on Error

    * @throws InterruptedException Is thrown on Interruption

    */

  @Override

  public boolean isTrue(Term[] input) throws KAON2Exception, InterruptedException {

      try {

          // Here we always get a term array with arity 1 (this is what we specified in getInfo()).

          if (BuiltinServices.isNumber(input[0])) {

              // Only return true if the condition matches.

              return true;

          }

      } catch (ClassCastException e) { // This exception cannot be thrown, as BuiltinServices.isNumber does a check.

           System.out.println("invalid parameters", e); // However, any error should be logged, no exception should be thrown just of invalid input.

      }

      return false; // Default return value for all non-matching input.

  }

  /**

    * This method will be called after evaluation for cleanup purposes.

    *

    * NOTE: It is possible that this operator will be used again when the same query object is openened again.

    * init() won't be called a second time.

    *

    * @throws KAON2Exception Is thrown on Error

    * @throws InterruptedException Is thrown on Interruption

    */

  @Override

  public void evaluationFinished() throws KAON2Exception, InterruptedException {

      // This built-in has just one state, no need to set it back.

      // It is used to set a built-in to the post-init() state.

  }

  /**

    * Method to get informations about this built-in. This method has to deliver a valid BuiltinSpec object

    * after the constructor finishes, before init() has be called.

    *

    * @return BuiltinSpec object with informations about this built-in.

    */

  @Override

  public BuiltinSpec getInfo() {

      BuiltinSpec.Builder builder = new BuiltinSpec.Builder(this, "isnumberdemo", 1);

      builder.setAllowedOntologyLanguages(OntologyLanguage.F_LOGIC);

      builder.setDescription("is X a number");

      builder.setParameters("X");

      return builder.build();

  }

  /**

    * Initialisation of this built-in. This is done for the instance before evaluation.

    *

    * @param context Contains additional Informations needed for some built-ins

    * @param args Term[] that contains the literal arguments.

    * @throws KAON2Exception Is thrown on Error

    * @throws InterruptedException Is thrown on Interruption

    */

  public void init(BuiltinContext context, Term[] args) throws KAON2Exception, InterruptedException {

      // Nothing to do, as this built-in does not need any initialization.

  }

  /**

    * Method to determine if a certain grounds configuration is evaluable.

    * This method has to be usable context free, init() is called in an other instance as

    * isEvaluable will be called. All needed information should be provided by the current

    * method arguments.

    *

    * @param grounds Object with grounding informations.

    * @param variableInstantiations This BitSets has the instantiation of all variables that occurs. Needed especially if partial-ground functions are used.

    * @param builtinContext BuiltinContext with additional context informations

    * @param args Literal arguments

    * @return if this grounds are evaluable or not.

    * @throws KAON2Exception Is thrown on Error

    */

  @Override

  public boolean isEvaluable(IGrounds grounds, BitSet variableInstantiations, BuiltinContext builtinContext, Term[] args) throws KAON2Exception {

      return grounds.getBitSet().get(0); // The one argument this built-in needs has to be ground

  }

}

Now take a look at MultiplyDemoBuiltin. It is a functional built-in, so it can deliver one result at most.

package com.ontoprise.builtin.functionalbuiltin;

import java.util.BitSet;

import org.apache.log4j.Logger;

import org.semanticweb.kaon2.api.KAON2Exception;

import org.semanticweb.kaon2.api.KAON2Manager;

import org.semanticweb.kaon2.api.OntologyLanguage;

import org.semanticweb.kaon2.api.logic.Term;

import com.ontoprise.builtin.BuiltinContext;

import com.ontoprise.builtin.BuiltinSpec;

import com.ontoprise.builtin.Grounds;

import com.ontoprise.builtin.builtin.BuiltinServices;

import com.ontoprise.builtin.interfaces.IFunctionalBuiltin;

import com.ontoprise.builtin.interfaces.IGrounds;

import com.ontoprise.util.OntopriseConstants;

/**

* Example built-in which multiplies two numbers.

*

*/

public class MultiplyDemoBuiltin implements IFunctionalBuiltin {

  protected static Logger _log = Logger.getLogger(OntopriseConstants.CORE_LOG);

  /**

    * Provide a no-argument constructor which needs nothing to do for this built-in.

    */

  public MultiplyDemoBuiltin() { // Empty as nothing is to do for this built-in

  }

  /**

    * This method is called during the evaluation. We must first check which of our four possible signatures applies and then either calculate the result or

    * check if a result is valid

    *

    * @param tuple the tuple which must be checked

    * @throws InterruptedException if the process was interrupted

    * @throws KAON2Exception if an error occurs

    */

  @Override

  public boolean evaluate(Term[] input, IGrounds grounds) throws KAON2Exception, InterruptedException {

      try {

          switch (grounds.getInt()) {

              case IGrounds.FIRSTSECONDTHIRD:

                  // All three positions are ground. This means we must check if "position 0" multiplied with "position 1"

                  // equals the "position 2" value. If this is true we send away the received tuple.

                  double op1 = BuiltinServices.getNumber(input[0]);

                  double op2 = BuiltinServices.getNumber(input[1]);

                  double result = BuiltinServices.getNumber(input[2]);

                  if (op1 * op2 == result) {

                      return true;

                  }

                  break;

              case IGrounds.FIRSTSECOND: {

                  // First and second position is ground. We must multiply the first with the second position

                  // and send away the result tuple.

                  op1 = BuiltinServices.getNumber(input[0]);

                  op2 = BuiltinServices.getNumber(input[1]);

                  // third argument must be filled with the result

                  result = op1 * op2;

                  // Create and send away the new tuple.

                  input[2] = KAON2Manager.factory().constantDouble(result);

                  return true;

              }

              case IGrounds.FIRSTTHIRD: {

                  // First and third position is ground. First position multiplied with second position

                  // must be equal to the third position. We need to calculate the second position.

                  op1 = BuiltinServices.getNumber(input[0]);

                  op2 = BuiltinServices.getNumber(input[1]);

                  // third argument must be filled with the result

                  result = op2 / op1;

                  // Create and send away the new tuple.

                  input[1] = KAON2Manager.factory().constantDouble(result);

                  return true;

              }

              case IGrounds.SECONDTHIRD: {

                  // Second and third position is ground. We must calculate the first position.

                  op1 = BuiltinServices.getNumber(input[1]);

                  op2 = BuiltinServices.getNumber(input[2]);

                  // third argument must be filled with the result

                  result = op2 / op1;

                  // Create and send away the new tuple.

                  input[0] = KAON2Manager.factory().constantDouble(result);

                  return true;

              }

          }

      } catch (Exception e) {

          _log.error("Invalid input", e);

      }

      return false;

  }

  /**

    * This method will be called after evaluation for cleanup purposes.

    *

    * NOTE: It is possible that this operator will be used again when the same query object is openened again.

    * init() won't be called a second time.

    *

    * @throws KAON2Exception Is thrown on Error

    * @throws InterruptedException Is thrown on Interruption

    */

  public void evaluationFinished() throws KAON2Exception, InterruptedException {

      // This built-in has just one state, no need to set it back.

      // It is used to set a built-in to the post-init() state.

  }

  /**

    * Method to get informations about this built-in. This method has to deliver a valid BuiltinSpec object

    * after the constructor finishes, before init() has be called.

    *

    * @return BuiltinSpec object with informations about this built-in.

    */

  public BuiltinSpec getInfo() {

      BuiltinSpec.Builder builder = new BuiltinSpec.Builder(this, "multiplydemo", 3);

      builder.setAllowedOntologyLanguages(OntologyLanguage.F_LOGIC);

      builder.setDescription("multiplydemo");

      builder.setParameters("first parameter", "second parameter", "result");

      return builder.build();

  }

  /**

    * Initialisation of this built-in. This is done for the instance before evaluation.

    *

    * @param context Contains additional Informations needed for some built-ins

    * @param args Term[] that contains the literal arguments.

    * @throws KAON2Exception Is thrown on Error

    * @throws InterruptedException Is thrown on Interruption

    */

  public void init(BuiltinContext context, Term[] args) throws KAON2Exception, InterruptedException {

      // Nothing to do, as this built-in does not need any initialization.

  }

  /**

    * Method to determine if a certain grounds configuration is evaluable.

    * This method has to be usable context free, init() is called in an other instance as

    * isEvaluable will be called. All needed information should be provided by the current

    * method arguments.

    *

    * @param grounds Object with grounding informations.

    * @param variableInstantiations This BitSets has the instantiation of all variables that occurs. Needed especially if partial-ground functions are used.

    * @param builtinContext BuiltinContext with additional context informations

    * @param args Literal arguments

    * @return if this grounds are evaluable or not.

    * @throws KAON2Exception Is thrown on Error

    */

  public boolean isEvaluable(IGrounds grounds, BitSet variableInstantiations, BuiltinContext builtinContext, Term[] args) throws KAON2Exception {

      // There are four different signatures that can be evaluated by this built-in.

      // It is just necessary that at least three of the arguments are ground.

      // This method has not to check the input type, just the grounds.

      switch (grounds.getInt()) {

          case Grounds.FIRSTSECONDTHIRD: {

              return true;

          }

          case Grounds.FIRSTSECOND: {

              return true;

          }

          case Grounds.FIRSTTHIRD: {

              return true;

          }

          case Grounds.SECONDTHIRD: {

              return true;

          }

          default:

              return false;

      }

  }

}

Take a look at a relational built-in. Square either computes X² for input X, or it returns sqrt(X) and -sqrt(X):

package com.ontoprise.builtin.relationalbuiltin.mathematics;

import java.util.BitSet;

import org.apache.log4j.Logger;

import org.semanticweb.kaon2.api.KAON2Exception;

import org.semanticweb.kaon2.api.KAON2Manager;

import org.semanticweb.kaon2.api.OntologyLanguage;

import org.semanticweb.kaon2.api.logic.Constant;

import org.semanticweb.kaon2.api.logic.Term;

import com.ontoprise.builtin.BuiltinContext;

import com.ontoprise.builtin.BuiltinSpec;

import com.ontoprise.builtin.interfaces.IGrounds;

import com.ontoprise.builtin.interfaces.IReceiver;

import com.ontoprise.builtin.interfaces.IRelationalBuiltin;

import com.ontoprise.util.OntopriseConstants;

public class Square implements IRelationalBuiltin {

  protected static Logger _log = Logger.getLogger(OntopriseConstants.CORE_LOG);

  /**

    * Provide a no-argument constructor which needs nothing to do for this built-in.

    */

  public Square() {

      // Empty as nothing is to do for this built-in

  }

  /**

    * Method to start evaluation. After this method has finished, any results have

    * to be sended by the IReceiver, no additional data is allowed.

    *

    * @param input Term[] with input tuples.

    * @param grounds IGrounds object with grounding informations.

    * @param destination Receiver to send every result to.

    *        Use destination.send(Term[] resultbuffer) to send results away.

    * @throws KAON2Exception Is thrown on Error

    * @throws InterruptedException Is thrown on Interruption

    */

  @Override

  public void evaluate(Term[] input, IGrounds grounds, IReceiver destination) throws KAON2Exception, InterruptedException {

      try {

          switch (grounds.getInt()) {

              case 3: {

                  if (!(((Constant) input[0]).getValue() instanceof Double) || !(((Constant) input[1]).getValue() instanceof Double)) {

                  }

                  double op1 = ((Double) ((Constant) input[0]).getValue()).doubleValue();

                  double result = ((Double) ((Constant) input[1]).getValue()).doubleValue();

                  if (op1 * op1 == result) {

                      destination.send(input); // sends the result away. The input Term[] is used as buffer.

                  }

                  break;

              }

              case 1: {

                  if (!(((Constant) input[0]).getValue() instanceof Double)) {

                  }

                  double op1 = ((Double) ((Constant) input[0]).getValue()).doubleValue();

                  double result = op1 * op1;

                  input[1] = KAON2Manager.factory().constant(new Double(result));

                  destination.send(input); // sends the result away. The input Term[] is used as buffer.

                  break;

              }

              case 2: {

                  if (!(((Constant) input[1]).getValue() instanceof Double)) {

                  }

                  double op2 = ((Double) ((Constant) input[1]).getValue()).doubleValue();

                  double result = Math.sqrt(op2);

                  input[0] = KAON2Manager.factory().constant(new Double(result));

                  destination.send(input); // sends the result away. The input Term[] is used as buffer.

                  input[0] = KAON2Manager.factory().constant(new Double(result * -1)); // just write the second result into the buffer, overwriting the first result.

                  destination.send(input); // sends the result away. The input Term[] is used as buffer.

                  break;

              }

              default:

          }

      } catch (ClassCastException e) { // catch exception for wrong input values. This is logical handled as false.

          _log.error("invalid input", e);

      }

  }

  /**

    * This method will be called after evaluation for cleanup purposes.

    *

    * NOTE: It is possible that this operator will be used again when the same

    * query object is openened again. init() won't be called a second time.

    *

    * @throws KAON2Exception Is thrown on Error

    * @throws InterruptedException Is thrown on Interruption

    */

  @Override

  public void evaluationFinished() throws KAON2Exception, InterruptedException {

      // This built-in has just one state, no need to set it back.

      // It is used to set a built-in to the post-init() state.

  }

  /**

    * Method to get informations about this built-in. This method has to deliver a

    * valid BuiltinSpec object after the constructor finishes, before init() has

    * be called.

    *

    * @return BuiltinSpec object with informations about this built-in.

    */

  @Override

  public BuiltinSpec getInfo() {

      BuiltinSpec.Builder builder = new BuiltinSpec.Builder(this, "square", 2);

      builder.setAllowedOntologyLanguages(OntologyLanguage.F_LOGIC);

      builder.setDescription("X^2 or sqrt(X) and -sqrt(X), depending of the grounds");

      builder.setParameters("X", "X^2");

      return builder.build();

  }

  /**

    * Initialisation of this built-in. This is done for the instance before evaluation.

    *

    * @param context Contains additional Informations needed for some built-ins

    * @param args Term[] that contains the literal arguments.

    * @throws KAON2Exception Is thrown on Error

    * @throws InterruptedException Is thrown on Interruption

    */

  @Override

  public void init(BuiltinContext context, Term[] args) throws KAON2Exception, InterruptedException {

      // Nothing to do, as this built-in does not need any initialization.

  }

  /**

    * Method to determine if a certain grounds configuration is evaluable. This method has to be

    * usable context free, init() is called in an other instance as

    * isEvaluable will be called. All needed information should be provided by the current method arguments.

    *

    * @param grounds Object with grounding informations.

    * @param variableInstantiations This BitSets has the instantiation of all variables that occurs.

    *        Needed especially if partial-ground functions are used.

    * @param builtinContext BuiltinContext with additional context informations

    * @param args Literal arguments

    * @return if this grounds are evaluable or not.

    * @throws KAON2Exception Is thrown on Error

    */

  @Override

  public boolean isEvaluable(IGrounds grounds, BitSet variableInstantiations, BuiltinContext builtinContext, Term[] args) throws KAON2Exception {

      // There are four different signatures that can be evaluated by this built-in.

      // It is just necessary that at least three of the arguments are ground.

      // This method has not to check the input type, just the grounds.

      switch (grounds.getInt()) {

          case 1: // first argument is ground, binary: 01

              return true;

          case 2: // second argument is ground, binary: 10

              return true;

          case 3: // both arguments are ground, binary: 11

              return true;

          default:

              return false; // cannot be evaluated

      }

  }