Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mapping function plugin system #152

Merged
merged 21 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
85b5cae
Added documentation of mapping function
jesper-friis Nov 12, 2023
fed4747
Added Triplestore.get_function() method
jesper-friis Nov 13, 2023
2b09a8f
Merge branch 'master' into 128-function-repo-plugin-system
jesper-friis Nov 23, 2023
35c7b84
Adding test for get_function
jesper-friis Nov 28, 2023
700a6f8
Added eval_function()
jesper-friis Dec 5, 2023
5f243be
update triplestore
jesper-friis Dec 11, 2023
d87fd9e
Fix typo
jesper-friis Dec 11, 2023
a31fff2
Added Triplestore._get_cost()
jesper-friis Dec 13, 2023
a22cee9
Merge branch 'master' of github.com:EMMC-ASBL/tripper into 128-functi…
jesper-friis Dec 14, 2023
a3c4188
Merge branch '128-function-repo-plugin-system' of github.com:EMMC-ASB…
jesper-friis Dec 14, 2023
668d415
Merge branch 'master' into 128-function-repo-plugin-system
jesper-friis Dec 21, 2023
3c5acc3
Added test for cost functions
jesper-friis Dec 21, 2023
2cd8d72
Merge branch '128-function-repo-plugin-system' of github.com:EMMC-ASB…
jesper-friis Dec 21, 2023
b126d93
Added support for function repo
jesper-friis Dec 21, 2023
38c3c23
Added return annotation to eval_function()
jesper-friis Dec 21, 2023
4c69229
Added pytest.importskip to test_eval_function.py
jesper-friis Dec 21, 2023
ffaf423
Remove comment
jesper-friis Dec 21, 2023
206f7db
Update tests/test_eval_function.py
jesper-friis Jan 24, 2024
beffea5
Merge branch 'master' into 128-function-repo-plugin-system
jesper-friis Jan 24, 2024
19365ce
Added a small correction
jesper-friis Jan 25, 2024
692a5a3
Fix order of things to check in if-statement
jesper-friis Jan 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,15 @@ def expected_function_triplestore(
@prefix ex: <http://example.com/onto#> .
@prefix fno: <https://w3id.org/function/ontology#> .
@prefix map: <http://emmo.info/domain-mappings#> .
@prefix oteio: <http://emmo.info/oteio#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

ex:sum__{fid} a fno:Function ;
rdfs:label "sum_"@en ;
oteio:hasPythonFunctionName "sum_" ;
oteio:hasPythonModuleName "conftest" ;
dcterms:description "Returns the sum of `first_param` and `second_param`."@en ;
fno:expects ( ex:sum__{fid}_parameter1_first_param ex:sum__{fid}_parameter2_second_param ) ;
fno:returns ( ex:sum__{fid}_output1 ) .
Expand Down
29 changes: 29 additions & 0 deletions tests/mappings/test_cost.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Test cost."""
import pytest

pytest.importorskip("rdflib")

# pylint: disable=wrong-import-position
from tripper import Triplestore

ts = Triplestore(backend="rdflib")

# Define some prefixed namespaces
CHEM = ts.bind("chem", "http://onto-ns.com/onto/chemistry#")
MOL = ts.bind("mol", "http://onto-ns.com/meta/0.1/Molecule#")
SUB = ts.bind("sub", "http://onto-ns.com/meta/0.1/Substance#")


def formula_cost(ts, input_iris, output_iri):
"""Returns a cost."""
# pylint: disable=unused-argument
return 2.72


# Add mappings from data models to ontology
ts.map(MOL.name, CHEM.Identifier, cost=3.14)
ts.map(SUB.formula, CHEM.Formula, cost=formula_cost)

# pylint: disable=protected-access
assert ts._get_cost(CHEM.Identifier) == 3.14
assert ts._get_cost(CHEM.Formula) == 2.72
9 changes: 9 additions & 0 deletions tests/test_add_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@ def func(a, b):
@prefix ex: <http://example.com/ex#> .
@prefix fno: <https://w3id.org/function/ontology#> .
@prefix map: <http://emmo.info/domain-mappings#> .
@prefix oteio: <http://emmo.info/oteio#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<:func_{f_id}> a fno:Function ;
rdfs:label "func"@en ;
oteio:hasPythonFunctionName "func" ;
oteio:hasPythonModuleName "test_add_function" ;
dcterms:description "Returns the sum of `a` and `b`."@en ;
fno:expects ( <:func_{f_id}_parameter1_a> <:func_{f_id}_parameter2_b> ) ;
fno:returns ( <:func_{f_id}_output1> ) .
Expand Down Expand Up @@ -67,13 +70,16 @@ def func(a, b):
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix emmo: <http://emmo.info/emmo#> .
@prefix ex: <http://example.com/ex#> .
@prefix oteio: <http://emmo.info/oteio#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<:func_{f_id}> a emmo:EMMO_4299e344_a321_4ef2_a744_bacfcce80afc ;
rdfs:label "func"@en ;
emmo:EMMO_36e69413_8c59_4799_946c_10b05d266e22 ex:arg1,
ex:arg2 ;
emmo:EMMO_c4bace1d_4db0_4cd3_87e9_18122bae2840 ex:sum ;
oteio:hasPythonFunctionName "func" ;
oteio:hasPythonModuleName "test_add_function" ;
dcterms:description "Returns the sum of `a` and `b`."@en .

ex:arg1 a emmo:EMMO_194e367c_9783_4bf5_96d0_9ad597d48d9a ;
Expand All @@ -100,13 +106,16 @@ def func(a, b):
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix emmo: <http://emmo.info/emmo#> .
@prefix ex: <http://example.com/ex#> .
@prefix oteio: <http://emmo.info/oteio#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<:func_{f_id}> a emmo:EMMO_4299e344_a321_4ef2_a744_bacfcce80afc ;
rdfs:label "func"@en ;
emmo:EMMO_36e69413_8c59_4799_946c_10b05d266e22 ex:arg1,
ex:arg2 ;
emmo:EMMO_c4bace1d_4db0_4cd3_87e9_18122bae2840 ex:sum ;
oteio:hasPythonFunctionName "func" ;
oteio:hasPythonModuleName "test_add_function" ;
dcterms:description "Returns the sum of `a` and `b`."@en .

ex:arg1 a emmo:EMMO_194e367c_9783_4bf5_96d0_9ad597d48d9a ;
Expand Down
53 changes: 53 additions & 0 deletions tests/test_eval_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""Test Triplestore.eval_function()"""
import pytest

from tripper import Triplestore

pytest.importorskip("rdflib")


def func(a, b):
"""Returns the sum of `a` and `b`."""
# pylint: disable=invalid-name
return a + b


ts = Triplestore(backend="rdflib")
EX = ts.bind("ex", "http://example.com/ex#")

# Test to add function in current scope
iri = ts.add_function(
func,
expects=[EX.arg1, EX.arg2],
returns=EX.sum,
standard="emmo",
)
assert ts.eval_function(func_iri=iri, args=(2, 3)) == 5


# Test to add a function from the standard library. The hashlib module
# is not expected to be imported in the current scope
iri2 = ts.add_function(
EX.shape256,
expects=[EX.Bytes],
returns=EX.ShakeVar,
func_name="shake_256",
module_name="hashlib",
)
shakevar = ts.eval_function(iri2, (b"a",))
assert shakevar.hexdigest(4) == "867e2cb0"


# Test to add a function from a pypi package.
iri3 = ts.add_function(
EX.UFloat,
expects=[EX.Uncertainty],
returns=EX.UUID,
func_name="ufloat",
module_name="uncertainties",
# package_name="uncertainties",
pypi_package_name="uncertainties==3.1.7",
)
val = ts.eval_function(iri3, args=(1, 0.1))
assert val.n == 1
assert val.s == 0.1
4 changes: 4 additions & 0 deletions tripper/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class NoSuchIRIError(NamespaceError):
"""Namespace has no such IRI."""


class CannotGetFunctionError(TriplestoreError):
"""Not able to get function documented in the triplestore."""


# === Warnings ===
class UnusedArgumentWarning(Warning):
"""Argument is unused."""
35 changes: 24 additions & 11 deletions tripper/mappings/mappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from pint import Quantity # remove

from tripper import DM, EMMO, FNO, MAP, RDF, RDFS
from tripper.errors import CannotGetFunctionError
from tripper.triplestore import hasAccessFunction, hasDataValue
from tripper.utils import parse_literal

Expand Down Expand Up @@ -889,17 +890,29 @@ def mapping_routes(
soAFun = dict(triplestore.subject_objects(hasAccessFunction))
soDVal = dict(triplestore.subject_objects(hasDataValue))

def getfunc(func_iri, default=None):
"""Returns callable function corresponding to `func_iri`.
Raises CannotGetFunctionError if func_iri cannot be found."""
if func_iri is None:
return None
if func_iri in function_repo and function_repo[func_iri]:
return function_repo[func_iri]
try:
return (
triplestore._get_function( # pylint: disable=protected-access
func_iri
)
)
except CannotGetFunctionError:
return default

def getcost(target, stepname):
"""Returns the cost assigned to IRI `target` for a mapping step
of type `stepname`."""
cost = soCost.get(target, default_costs[stepname])
if cost is None:
return None
return (
function_repo[cost]
if cost in function_repo
else float(parse_literal(cost))
)
if cost is None or callable(cost) or isinstance(cost, float):
return cost
return getfunc(cost, float(parse_literal(cost)))

def walk(target, visited, step):
"""Walk backward in rdf graph from `node` to sources."""
Expand All @@ -914,7 +927,7 @@ def addnode(node, steptype, stepname):
step.cost = getcost(target, stepname)
if node in soAFun:
value = value_class(
value=triplestore.function_repo[soAFun[node]],
value=getfunc(soAFun[node]),
unit=soUnit.get(node),
iri=node,
property_iri=soInst.get(node),
Expand Down Expand Up @@ -962,10 +975,10 @@ def addnode(node, steptype, stepname):
addnode(node, StepType.INV_SUBCLASSOF, "subClassOf")

for fmap in function_mappers:
for func, input_iris in fmap(triplestore)[target]:
for func_iri, input_iris in fmap(triplestore)[target]:
step.steptype = StepType.FUNCTION
step.cost = getcost(func, "function")
step.function = function_repo.get(func)
step.cost = getcost(func_iri, "function")
step.function = getfunc(func_iri)
step.join_mode = True
for input_iri in input_iris:
step0 = mappingstep_class(
Expand Down
1 change: 1 addition & 0 deletions tripper/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,4 @@ def __eq__(self, other):
EMMO = Namespace("http://emmo.info/emmo#")
MAP = Namespace("http://emmo.info/domain-mappings#")
DM = Namespace("http://emmo.info/datamodel#")
OTEIO = Namespace("http://emmo.info/oteio#")
Loading