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

Confirm interpretation of any_of constraints is correct #2

Open
eecavanna opened this issue Jul 15, 2024 · 3 comments
Open

Confirm interpretation of any_of constraints is correct #2

eecavanna opened this issue Jul 15, 2024 · 3 comments
Assignees
Labels
question Further information is requested

Comments

@eecavanna
Copy link
Collaborator

eecavanna commented Jul 15, 2024

Today, if a slot has the any_of constraint defined on it, refscan completely disregards the slot's range.

refscan/refscan/refscan.py

Lines 168 to 188 in d7d0edd

# Determine the slot's "effective" range, by taking into account its `any_of` constraints (if defined).
#
# Note: The `any_of` constraints constrain the slot's "effective" range beyond that described by the
# induced slot definition's `range` attribute. `SchemaView` does not seem to provide the result
# of applying those additional constraints, so we do it manually here (if any are defined).
#
# Reference: https://github.com/orgs/linkml/discussions/2101#discussion-6625646
#
names_of_eligible_target_classes: list[str] = []
if "any_of" in slot_definition and len(slot_definition.any_of) > 0: # use the `any_of` attribute
for slot_expression in slot_definition.any_of:
if slot_expression.range in schema_view.all_classes():
own_and_descendant_class_names = schema_view.class_descendants(slot_expression.range)
names_of_eligible_target_classes.extend(own_and_descendant_class_names)
else: # use the `range` attribute
if slot_definition.range not in schema_view.all_classes(): # if it's not a class name, abort
continue
else:
# Get the specified class name and the names of all classes that inherit from it.
own_and_descendant_class_names = schema_view.class_descendants(slot_definition.range)
names_of_eligible_target_classes.extend(own_and_descendant_class_names)

I implemented it that way because I was under the impression that the any_of constraint was used to further constrain the slot's range, beyond the constraints already placed on the slot by the slot's range. In other words, I was under the impression any_of would always be a more specific set than range (in which case, I wondered why a schema author would bother specifying a range on that slot in the first place).

I'm creating this ticket to represent the task of getting an authoritative answer to the following question:

  1. Is ignoring the range in the presence of any_of, consistent with how people proficient with LinkML would interpret a LinkML schema?

Related (upstream): linkml/linkml#2103

@eecavanna eecavanna added the question Further information is requested label Jul 15, 2024
@eecavanna eecavanna self-assigned this Jul 15, 2024
@sierra-moxon
Copy link
Member

"used to further constrain the slot's range" is also how I would interpret the range. However, if a slot range is further constrained by slot_usage, the more general range is still valid, just redundant. That's why some of the methods in schema view return a list of ranges, e.g. ["NamedThing," "PlannedProcess"] -- both of these would be "true" -- but "PlannedProcess" is more specific (when we're validating, we don't want a range of "OntologyClass" to validate. This means we want a method that returns the "most specific slot range in the context of this class." The induced_slot method in LinkML can be this method, but it is currently not taking into account the anyOf or oneOf constructs, and there is a discussion about whether this should be the default instead of in another method. I think the current understanding is that this anyOf/oneOf functionality should start in the induced_slot method).

@eecavanna
Copy link
Collaborator Author

Thank you, @sierra-moxon, for writing that up and also helping me understand the situation in other conversations.

@eecavanna
Copy link
Collaborator Author

eecavanna commented Jul 19, 2024

I moved the code that handles any_of into a helper function in an attempt to facilitate testing it. The helper function is named get_names_of_classes_in_effective_range_of_slot (link) and is in the file refscan/lib/helpers.py. After moving it to a helper function, I implemented a test targeting that function, in an attempt to demonstrate to people how refscan currently treats any_of (when it's present on a slot). The test is named test_get_names_of_classes_in_effective_range_of_slot (link) and is in the file tests/test_helpers.py.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants