Skip to content

Commit

Permalink
Add custom numeric functions log and exp
Browse files Browse the repository at this point in the history
There does not seem to be a standard prefix for those, so I am using
`<http://qlever.cs.uni-freiburg.de/function#>` for now (abbreviated as
prefix `qfn` in the QLever UI).
  • Loading branch information
Hannah Bast committed Aug 2, 2023
1 parent d50499f commit 2e0a1d4
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/engine/sparqlExpressions/NaryExpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ SparqlExpression::Ptr makeRoundExpression(SparqlExpression::Ptr child);
SparqlExpression::Ptr makeAbsExpression(SparqlExpression::Ptr child);
SparqlExpression::Ptr makeCeilExpression(SparqlExpression::Ptr child);
SparqlExpression::Ptr makeFloorExpression(SparqlExpression::Ptr child);
SparqlExpression::Ptr makeLogExpression(SparqlExpression::Ptr child);
SparqlExpression::Ptr makeExpExpression(SparqlExpression::Ptr child);

SparqlExpression::Ptr makeDistExpression(SparqlExpression::Ptr child1,
SparqlExpression::Ptr child2);
Expand Down
29 changes: 29 additions & 0 deletions src/engine/sparqlExpressions/NumericUnaryExpressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,29 @@ inline const auto floorImpl = []<typename T>(T num) {
};
inline const auto floor = makeNumericExpression<decltype(floorImpl)>();
using FloorExpression = NARY<1, FV<decltype(floor), NumericValueGetter>>;

// Natural Logarithm.
inline const auto logImpl = []<typename T>(T num) {
if constexpr (std::is_floating_point_v<T> || std::is_integral_v<T>) {
return num > 0 ? std::log(num) : std::numeric_limits<double>::quiet_NaN();
} else {
return Id::makeUndefined();
}
};
inline const auto log = makeNumericExpression<decltype(logImpl)>();
using LogExpression = NARY<1, FV<decltype(log), NumericValueGetter>>;

// Exponentiation.
inline const auto expImpl = []<typename T>(T num) {
if constexpr (std::is_floating_point_v<T> || std::is_integral_v<T>) {
return std::exp(num);
} else {
return Id::makeUndefined();
}
};
inline const auto exp = makeNumericExpression<decltype(expImpl)>();
using ExpExpression = NARY<1, FV<decltype(exp), NumericValueGetter>>;

} // namespace detail

using namespace detail;
Expand All @@ -84,6 +107,12 @@ SparqlExpression::Ptr makeCeilExpression(SparqlExpression::Ptr child) {
SparqlExpression::Ptr makeFloorExpression(SparqlExpression::Ptr child) {
return std::make_unique<FloorExpression>(std::move(child));
}
SparqlExpression::Ptr makeLogExpression(SparqlExpression::Ptr child) {
return std::make_unique<LogExpression>(std::move(child));
}
SparqlExpression::Ptr makeExpExpression(SparqlExpression::Ptr child) {
return std::make_unique<ExpExpression>(std::move(child));
}

SparqlExpression::Ptr makeUnaryMinusExpression(SparqlExpression::Ptr child) {
return std::make_unique<UnaryMinusExpression>(std::move(child));
Expand Down
16 changes: 15 additions & 1 deletion src/parser/sparqlParser/SparqlQleverVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ ExpressionPtr Visitor::processIriFunctionCall(

constexpr static std::string_view geofPrefix =
"<http://www.opengis.net/def/function/geosparql/";
if (std::string_view iriView = iri; iriView.starts_with(geofPrefix)) {
constexpr static std::string_view qfnPrefix =
"<http://qlever.cs.uni-freiburg.de/function#";
std::string_view iriView = iri;
if (iriView.starts_with(geofPrefix)) {
iriView.remove_prefix(geofPrefix.size());
AD_CONTRACT_CHECK(iriView.ends_with('>'));
iriView.remove_suffix(1);
Expand All @@ -87,6 +90,17 @@ ExpressionPtr Visitor::processIriFunctionCall(
checkNumArgs("geof:", iriView, 1);
return sparqlExpression::makeLatitudeExpression(std::move(argList[0]));
}
} else if (iriView.starts_with(qfnPrefix)) {
iriView.remove_prefix(qfnPrefix.size());
AD_CONTRACT_CHECK(iriView.ends_with('>'));
iriView.remove_suffix(1);

Check warning on line 96 in src/parser/sparqlParser/SparqlQleverVisitor.cpp

View check run for this annotation

Codecov / codecov/patch

src/parser/sparqlParser/SparqlQleverVisitor.cpp#L94-L96

Added lines #L94 - L96 were not covered by tests
if (iriView == "log") {
checkNumArgs("qfn:", iriView, 1);
return sparqlExpression::makeLogExpression(std::move(argList[0]));

Check warning on line 99 in src/parser/sparqlParser/SparqlQleverVisitor.cpp

View check run for this annotation

Codecov / codecov/patch

src/parser/sparqlParser/SparqlQleverVisitor.cpp#L98-L99

Added lines #L98 - L99 were not covered by tests
} else if (iriView == "exp") {
checkNumArgs("qfn:", iriView, 1);
return sparqlExpression::makeExpExpression(std::move(argList[0]));
}

Check warning on line 103 in src/parser/sparqlParser/SparqlQleverVisitor.cpp

View check run for this annotation

Codecov / codecov/patch

src/parser/sparqlParser/SparqlQleverVisitor.cpp#L101-L103

Added lines #L101 - L103 were not covered by tests
}
reportNotSupported(ctx, "Function \"" + iri + "\" is");
}
Expand Down
14 changes: 13 additions & 1 deletion test/SparqlExpressionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,8 @@ TEST(SparqlExpression, unaryMinus) {
checkMinus(Strings{"true", "false", "", "<blibb>"}, Ids{U, U, U, U});
}

TEST(SparqlExpression, ceilFloorAbsRound) {
// Test the built-in numeric functions (floor, abs, round, ceil).
TEST(SparqlExpression, builtInNumericFunctions) {
auto bindUnary = [](auto f) {
return std::bind_front(testUnaryExpression, f);
};
Expand Down Expand Up @@ -468,6 +469,17 @@ TEST(SparqlExpression, ceilFloorAbsRound) {
checkRound(input, round);
}

// Test the custom numeric functions implemented so far (log, exp).
TEST(SparqlExpression, customNumericFunctions) {
auto nan = std::numeric_limits<double>::quiet_NaN();
testUnaryExpression(makeLogExpression,
std::vector<Id>{B(false), B(true), I(1), D(exp(1)), U},
std::vector<Id>{D(nan), D(0), D(0), D(1), U});
testUnaryExpression(makeExpExpression,
std::vector<Id>{B(false), B(true), I(0), D(1), U},
std::vector<Id>{D(1), D(exp(1)), D(1), D(exp(1)), U});
}

// ________________________________________________________________________________________
TEST(SparqlExpression, geoSparqlExpressions) {
auto checkLat = std::bind_front(testUnaryExpression, &makeLatitudeExpression);
Expand Down

0 comments on commit 2e0a1d4

Please sign in to comment.