Fix integer detection
Clingo treats operations that were assumed to be “invalid” not as processing errors but as operations returning an empty set. This changes how formulas have to be evaluated. This commit implements an explicit function for retrieving the return type of an expression, that is, both the domain of the result as well as whether it’s an empty, unit, or general set with multiple values.
This commit is contained in:
@@ -1,263 +0,0 @@
|
||||
#ifndef __ANTHEM__ARITHMETICS_H
|
||||
#define __ANTHEM__ARITHMETICS_H
|
||||
|
||||
#include <anthem/AST.h>
|
||||
#include <anthem/Utils.h>
|
||||
|
||||
namespace anthem
|
||||
{
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Arithmetics
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct DefaultVariableDomainAccessor
|
||||
{
|
||||
Domain operator()(const ast::Variable &variable)
|
||||
{
|
||||
return variable.declaration->domain;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class VariableDomainAccessor = DefaultVariableDomainAccessor, class... Arguments>
|
||||
EvaluationResult isArithmetic(const ast::Term &term, Arguments &&...);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class VariableDomainAccessor = DefaultVariableDomainAccessor>
|
||||
struct IsTermArithmeticVisitor
|
||||
{
|
||||
template <class... Arguments>
|
||||
static EvaluationResult visit(const ast::BinaryOperation &binaryOperation, Arguments &&... arguments)
|
||||
{
|
||||
const auto isLeftArithemtic = isArithmetic<VariableDomainAccessor>(binaryOperation.left, std::forward<Arguments>(arguments)...);
|
||||
const auto isRightArithmetic = isArithmetic<VariableDomainAccessor>(binaryOperation.right, std::forward<Arguments>(arguments)...);
|
||||
|
||||
if (isLeftArithemtic == EvaluationResult::Error || isRightArithmetic == EvaluationResult::Error)
|
||||
return EvaluationResult::Error;
|
||||
|
||||
if (isLeftArithemtic == EvaluationResult::False || isRightArithmetic == EvaluationResult::False)
|
||||
return EvaluationResult::Error;
|
||||
|
||||
if (isLeftArithemtic == EvaluationResult::Unknown || isRightArithmetic == EvaluationResult::Unknown)
|
||||
return EvaluationResult::Unknown;
|
||||
|
||||
return EvaluationResult::True;
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static EvaluationResult visit(const ast::Boolean &, Arguments &&...)
|
||||
{
|
||||
return EvaluationResult::False;
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static EvaluationResult visit(const ast::Function &function, Arguments &&...)
|
||||
{
|
||||
switch (function.declaration->domain)
|
||||
{
|
||||
case Domain::Noninteger:
|
||||
return EvaluationResult::False;
|
||||
case Domain::Integer:
|
||||
return EvaluationResult::True;
|
||||
case Domain::Unknown:
|
||||
return EvaluationResult::Unknown;
|
||||
}
|
||||
|
||||
return EvaluationResult::Unknown;
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static EvaluationResult visit(const ast::Integer &, Arguments &&...)
|
||||
{
|
||||
return EvaluationResult::True;
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static EvaluationResult visit(const ast::Interval &interval, Arguments &&... arguments)
|
||||
{
|
||||
const auto isFromArithmetic = isArithmetic<VariableDomainAccessor>(interval.from, std::forward<Arguments>(arguments)...);
|
||||
const auto isToArithmetic = isArithmetic<VariableDomainAccessor>(interval.to, std::forward<Arguments>(arguments)...);
|
||||
|
||||
if (isFromArithmetic == EvaluationResult::Error || isToArithmetic == EvaluationResult::Error)
|
||||
return EvaluationResult::Error;
|
||||
|
||||
if (isFromArithmetic == EvaluationResult::False || isToArithmetic == EvaluationResult::False)
|
||||
return EvaluationResult::Error;
|
||||
|
||||
if (isFromArithmetic == EvaluationResult::Unknown || isToArithmetic == EvaluationResult::Unknown)
|
||||
return EvaluationResult::Unknown;
|
||||
|
||||
return EvaluationResult::True;
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static EvaluationResult visit(const ast::SpecialInteger &, Arguments &&...)
|
||||
{
|
||||
return EvaluationResult::False;
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static EvaluationResult visit(const ast::String &, Arguments &&...)
|
||||
{
|
||||
return EvaluationResult::False;
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static EvaluationResult visit(const ast::UnaryOperation &unaryOperation, Arguments &&... arguments)
|
||||
{
|
||||
const auto isArgumentArithmetic = isArithmetic<VariableDomainAccessor>(unaryOperation.argument, std::forward<Arguments>(arguments)...);
|
||||
|
||||
switch (unaryOperation.operator_)
|
||||
{
|
||||
case ast::UnaryOperation::Operator::Absolute:
|
||||
return (isArgumentArithmetic == EvaluationResult::False ? EvaluationResult::Error : isArgumentArithmetic);
|
||||
}
|
||||
|
||||
return EvaluationResult::Unknown;
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static EvaluationResult visit(const ast::Variable &variable, Arguments &&... arguments)
|
||||
{
|
||||
const auto domain = VariableDomainAccessor()(variable, std::forward<Arguments>(arguments)...);
|
||||
|
||||
switch (domain)
|
||||
{
|
||||
case Domain::Noninteger:
|
||||
return EvaluationResult::False;
|
||||
case Domain::Integer:
|
||||
return EvaluationResult::True;
|
||||
case Domain::Unknown:
|
||||
return EvaluationResult::Unknown;
|
||||
}
|
||||
|
||||
return EvaluationResult::Unknown;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class VariableDomainAccessor, class... Arguments>
|
||||
EvaluationResult isArithmetic(const ast::Term &term, Arguments &&... arguments)
|
||||
{
|
||||
return term.accept(IsTermArithmeticVisitor<VariableDomainAccessor>(), std::forward<Arguments>(arguments)...);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EvaluationResult isInteger(const ast::Term &term);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct IsTermIntegerVisitor
|
||||
{
|
||||
static EvaluationResult visit(const ast::BinaryOperation &binaryOperation)
|
||||
{
|
||||
const auto isLeftArithemtic = isArithmetic(binaryOperation.left);
|
||||
const auto isRightArithmetic = isArithmetic(binaryOperation.right);
|
||||
|
||||
if (isLeftArithemtic == EvaluationResult::Error || isRightArithmetic == EvaluationResult::Error)
|
||||
return EvaluationResult::Error;
|
||||
|
||||
if (isLeftArithemtic == EvaluationResult::False || isRightArithmetic == EvaluationResult::False)
|
||||
return EvaluationResult::Error;
|
||||
|
||||
if (binaryOperation.operator_ == ast::BinaryOperation::Operator::Division)
|
||||
return EvaluationResult::False;
|
||||
|
||||
if (isLeftArithemtic == EvaluationResult::Unknown || isRightArithmetic == EvaluationResult::Unknown)
|
||||
return EvaluationResult::Unknown;
|
||||
|
||||
return EvaluationResult::True;
|
||||
}
|
||||
|
||||
static EvaluationResult visit(const ast::Boolean &)
|
||||
{
|
||||
return EvaluationResult::False;
|
||||
}
|
||||
|
||||
static EvaluationResult visit(const ast::Function &function)
|
||||
{
|
||||
switch (function.declaration->domain)
|
||||
{
|
||||
case Domain::Noninteger:
|
||||
return EvaluationResult::False;
|
||||
case Domain::Integer:
|
||||
return EvaluationResult::True;
|
||||
case Domain::Unknown:
|
||||
return EvaluationResult::Unknown;
|
||||
}
|
||||
|
||||
return EvaluationResult::Unknown;
|
||||
}
|
||||
|
||||
static EvaluationResult visit(const ast::Integer &)
|
||||
{
|
||||
return EvaluationResult::True;
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static EvaluationResult visit(const ast::Interval &)
|
||||
{
|
||||
return EvaluationResult::False;
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static EvaluationResult visit(const ast::SpecialInteger &)
|
||||
{
|
||||
return EvaluationResult::False;
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static EvaluationResult visit(const ast::String &)
|
||||
{
|
||||
return EvaluationResult::False;
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static EvaluationResult visit(const ast::UnaryOperation &unaryOperation)
|
||||
{
|
||||
const auto isArgumentArithmetic = isArithmetic(unaryOperation.argument);
|
||||
|
||||
switch (unaryOperation.operator_)
|
||||
{
|
||||
case ast::UnaryOperation::Operator::Absolute:
|
||||
return (isArgumentArithmetic == EvaluationResult::False ? EvaluationResult::Error : isArgumentArithmetic);
|
||||
}
|
||||
|
||||
return EvaluationResult::Unknown;
|
||||
}
|
||||
|
||||
static EvaluationResult visit(const ast::Variable &variable)
|
||||
{
|
||||
switch (variable.declaration->domain)
|
||||
{
|
||||
case Domain::Noninteger:
|
||||
return EvaluationResult::False;
|
||||
case Domain::Integer:
|
||||
return EvaluationResult::True;
|
||||
case Domain::Unknown:
|
||||
return EvaluationResult::Unknown;
|
||||
}
|
||||
|
||||
return EvaluationResult::Unknown;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline EvaluationResult isInteger(const ast::Term &term)
|
||||
{
|
||||
return term.accept(IsTermIntegerVisitor());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
}
|
||||
|
||||
#endif
|
166
include/anthem/Type.h
Normal file
166
include/anthem/Type.h
Normal file
@@ -0,0 +1,166 @@
|
||||
#ifndef __ANTHEM__ARITHMETICS_H
|
||||
#define __ANTHEM__ARITHMETICS_H
|
||||
|
||||
#include <anthem/AST.h>
|
||||
#include <anthem/Utils.h>
|
||||
|
||||
namespace anthem
|
||||
{
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Arithmetics
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct DefaultVariableDomainAccessor
|
||||
{
|
||||
Domain operator()(const ast::Variable &variable)
|
||||
{
|
||||
return variable.declaration->domain;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class VariableDomainAccessor = DefaultVariableDomainAccessor, class... Arguments>
|
||||
Type type(const ast::Term &term, Arguments &&...);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class VariableDomainAccessor = DefaultVariableDomainAccessor>
|
||||
struct TermTypeVisitor
|
||||
{
|
||||
template <class... Arguments>
|
||||
static Type visit(const ast::BinaryOperation &binaryOperation, Arguments &&... arguments)
|
||||
{
|
||||
const auto leftType = type<VariableDomainAccessor>(binaryOperation.left, std::forward<Arguments>(arguments)...);
|
||||
const auto rightType = type<VariableDomainAccessor>(binaryOperation.right, std::forward<Arguments>(arguments)...);
|
||||
|
||||
// Binary operations on empty sets return an empty set (also with division)
|
||||
if (leftType.setSize == SetSize::Empty || rightType.setSize == SetSize::Empty)
|
||||
return {Domain::Unknown, SetSize::Empty};
|
||||
|
||||
// Binary operations on nonintegers return an empty set (also with division)
|
||||
if (leftType.domain == Domain::Noninteger || rightType.domain == Domain::Noninteger)
|
||||
return {Domain::Unknown, SetSize::Empty};
|
||||
|
||||
// Binary operations on unknown types return an unknown set
|
||||
if (leftType.domain == Domain::Unknown || rightType.domain == Domain::Unknown)
|
||||
return {Domain::Unknown, SetSize::Unknown};
|
||||
|
||||
// Divisions return an unknown set
|
||||
if (binaryOperation.operator_ == ast::BinaryOperation::Operator::Division)
|
||||
return {Domain::Integer, SetSize::Unknown};
|
||||
|
||||
// Binary operations on integer sets of unknown size return an integer set of unknown size
|
||||
if (leftType.setSize == SetSize::Unknown || rightType.setSize == SetSize::Unknown)
|
||||
return {Domain::Integer, SetSize::Unknown};
|
||||
|
||||
// Binary operations on integer sets with multiple elements return an integer set with multiple elements
|
||||
if (leftType.setSize == SetSize::Multi || rightType.setSize == SetSize::Multi)
|
||||
return {Domain::Integer, SetSize::Multi};
|
||||
|
||||
// Binary operations on plain integers return a plain integer
|
||||
return {Domain::Integer, SetSize::Unit};
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static Type visit(const ast::Boolean &, Arguments &&...)
|
||||
{
|
||||
return {Domain::Noninteger, SetSize::Unit};
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static Type visit(const ast::Function &function, Arguments &&...)
|
||||
{
|
||||
// TODO: check that functions cannot return sets
|
||||
|
||||
return {function.declaration->domain, SetSize::Unit};
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static Type visit(const ast::Integer &, Arguments &&...)
|
||||
{
|
||||
return {Domain::Integer, SetSize::Unit};
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static Type visit(const ast::Interval &interval, Arguments &&... arguments)
|
||||
{
|
||||
const auto fromType = type<VariableDomainAccessor>(interval.from, std::forward<Arguments>(arguments)...);
|
||||
const auto toType = type<VariableDomainAccessor>(interval.to, std::forward<Arguments>(arguments)...);
|
||||
|
||||
// Intervals with empty sets return an empty set
|
||||
if (fromType.setSize == SetSize::Empty || toType.setSize == SetSize::Empty)
|
||||
return {Domain::Unknown, SetSize::Empty};
|
||||
|
||||
// Intervals with nonintegers return an empty set
|
||||
if (fromType.domain == Domain::Noninteger || toType.domain == Domain::Noninteger)
|
||||
return {Domain::Unknown, SetSize::Empty};
|
||||
|
||||
// Intervals with unknown types return an unknown set
|
||||
if (fromType.domain == Domain::Unknown || toType.domain == Domain::Unknown)
|
||||
return {Domain::Unknown, SetSize::Unknown};
|
||||
|
||||
// Intervals with integers generally return integer sets
|
||||
// TODO: handle 1-element intervals such as 1..1 and empty intervals such as 2..1
|
||||
return {Domain::Integer, SetSize::Unknown};
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static Type visit(const ast::SpecialInteger &, Arguments &&...)
|
||||
{
|
||||
return {Domain::Noninteger, SetSize::Unit};
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static Type visit(const ast::String &, Arguments &&...)
|
||||
{
|
||||
return {Domain::Noninteger, SetSize::Unit};
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static Type visit(const ast::UnaryOperation &unaryOperation, Arguments &&... arguments)
|
||||
{
|
||||
assert(unaryOperation.operator_ == ast::UnaryOperation::Operator::Absolute);
|
||||
|
||||
const auto argumentType = type<VariableDomainAccessor>(unaryOperation.argument, std::forward<Arguments>(arguments)...);
|
||||
|
||||
// Absolute value of an empty set returns an empty set
|
||||
if (argumentType.setSize == SetSize::Empty)
|
||||
return {Domain::Unknown, SetSize::Empty};
|
||||
|
||||
// Absolute value of nonintegers returns an empty set
|
||||
if (argumentType.domain == Domain::Noninteger)
|
||||
return {Domain::Unknown, SetSize::Empty};
|
||||
|
||||
// Absolute value of integers returns the same type
|
||||
if (argumentType.domain == Domain::Integer)
|
||||
return argumentType;
|
||||
|
||||
return {Domain::Unknown, SetSize::Unknown};
|
||||
}
|
||||
|
||||
template <class... Arguments>
|
||||
static Type visit(const ast::Variable &variable, Arguments &&... arguments)
|
||||
{
|
||||
const auto domain = VariableDomainAccessor()(variable, std::forward<Arguments>(arguments)...);
|
||||
|
||||
return {domain, SetSize::Unit};
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class VariableDomainAccessor, class... Arguments>
|
||||
Type type(const ast::Term &term, Arguments &&... arguments)
|
||||
{
|
||||
return term.accept(TermTypeVisitor<VariableDomainAccessor>(), std::forward<Arguments>(arguments)...);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -52,6 +52,24 @@ enum class Domain
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum class SetSize
|
||||
{
|
||||
Empty,
|
||||
Unit,
|
||||
Multi,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct Type
|
||||
{
|
||||
Domain domain{Domain::Unknown};
|
||||
SetSize setSize{SetSize::Unknown};
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user