From abf1b0e6c015b9dd0dddd3f75de49cd53fdfa6f9 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 01:17:39 -0700 Subject: [PATCH] update hdmf models/schema --- .../src/nwb_linkml/generators/pydantic.py | 1 + nwb_linkml/src/nwb_linkml/includes/hdmf.py | 94 ++-- nwb_linkml/src/nwb_linkml/io/schema.py | 4 +- .../hdmf_common/v1_1_0/hdmf_common_table.py | 246 +++++++++- .../hdmf_common/v1_1_2/hdmf_common_table.py | 246 +++++++++- .../hdmf_common/v1_1_3/hdmf_common_table.py | 246 +++++++++- .../pydantic/hdmf_common/v1_2_0/__init__.py | 1 + .../hdmf_common/v1_2_0/hdmf_common_base.py | 88 ++++ .../hdmf_common/v1_2_0/hdmf_common_sparse.py | 125 +++++ .../hdmf_common/v1_2_0/hdmf_common_table.py | 449 +++++++++++++++++ .../pydantic/hdmf_common/v1_2_0/namespace.py | 83 ++++ .../pydantic/hdmf_common/v1_2_1/__init__.py | 1 + .../hdmf_common/v1_2_1/hdmf_common_base.py | 104 ++++ .../hdmf_common/v1_2_1/hdmf_common_sparse.py | 126 +++++ .../hdmf_common/v1_2_1/hdmf_common_table.py | 449 +++++++++++++++++ .../pydantic/hdmf_common/v1_2_1/namespace.py | 83 ++++ .../pydantic/hdmf_common/v1_3_0/__init__.py | 1 + .../hdmf_common/v1_3_0/hdmf_common_base.py | 104 ++++ .../v1_3_0/hdmf_common_resources.py | 181 +++++++ .../hdmf_common/v1_3_0/hdmf_common_sparse.py | 110 +++++ .../hdmf_common/v1_3_0/hdmf_common_table.py | 449 +++++++++++++++++ .../pydantic/hdmf_common/v1_3_0/namespace.py | 86 ++++ .../pydantic/hdmf_common/v1_4_0/__init__.py | 1 + .../hdmf_common/v1_4_0/hdmf_common_base.py | 104 ++++ .../hdmf_common/v1_4_0/hdmf_common_sparse.py | 110 +++++ .../hdmf_common/v1_4_0/hdmf_common_table.py | 422 ++++++++++++++++ .../pydantic/hdmf_common/v1_4_0/namespace.py | 77 +++ .../hdmf_common/v1_5_0/hdmf_common_table.py | 246 +++++++++- .../pydantic/hdmf_common/v1_5_1/__init__.py | 1 + .../hdmf_common/v1_5_1/hdmf_common_base.py | 104 ++++ .../hdmf_common/v1_5_1/hdmf_common_sparse.py | 110 +++++ .../hdmf_common/v1_5_1/hdmf_common_table.py | 453 ++++++++++++++++++ .../pydantic/hdmf_common/v1_5_1/namespace.py | 78 +++ .../pydantic/hdmf_common/v1_6_0/__init__.py | 1 + .../hdmf_common/v1_6_0/hdmf_common_base.py | 104 ++++ .../hdmf_common/v1_6_0/hdmf_common_sparse.py | 110 +++++ .../hdmf_common/v1_6_0/hdmf_common_table.py | 453 ++++++++++++++++++ .../pydantic/hdmf_common/v1_6_0/namespace.py | 78 +++ .../pydantic/hdmf_common/v1_7_0/__init__.py | 1 + .../hdmf_common/v1_7_0/hdmf_common_base.py | 104 ++++ .../hdmf_common/v1_7_0/hdmf_common_sparse.py | 110 +++++ .../hdmf_common/v1_7_0/hdmf_common_table.py | 453 ++++++++++++++++++ .../pydantic/hdmf_common/v1_7_0/namespace.py | 78 +++ .../hdmf_common/v1_8_0/hdmf_common_table.py | 246 +++++++++- .../v0_1_0/hdmf_experimental_experimental.py | 4 +- .../v0_1_0/hdmf_experimental_resources.py | 4 +- .../hdmf_experimental/v0_1_0/namespace.py | 7 +- .../hdmf_experimental/v0_2_0/__init__.py | 1 + .../v0_2_0/hdmf_experimental_experimental.py | 93 ++++ .../v0_2_0/hdmf_experimental_resources.py | 199 ++++++++ .../hdmf_experimental/v0_2_0/namespace.py | 89 ++++ .../hdmf_experimental/v0_3_0/__init__.py | 1 + .../v0_3_0/hdmf_experimental_experimental.py | 93 ++++ .../v0_3_0/hdmf_experimental_resources.py | 207 ++++++++ .../hdmf_experimental/v0_3_0/namespace.py | 89 ++++ .../hdmf_experimental/v0_4_0/__init__.py | 1 + .../v0_4_0/hdmf_experimental_experimental.py | 93 ++++ .../v0_4_0/hdmf_experimental_resources.py | 229 +++++++++ .../hdmf_experimental/v0_4_0/namespace.py | 90 ++++ .../v1_1_0/hdmf-common.nwb.language.yaml | 2 +- .../v1_1_2/hdmf-common.nwb.language.yaml | 2 +- .../v1_1_3/hdmf-common.nwb.language.yaml | 2 +- .../hdmf_common/v1_2_0/hdmf-common.base.yaml | 33 ++ .../v1_2_0/hdmf-common.nwb.language.yaml | 109 +++++ .../v1_2_0/hdmf-common.sparse.yaml | 75 +++ .../hdmf_common/v1_2_0/hdmf-common.table.yaml | 181 +++++++ .../linkml/hdmf_common/v1_2_0/namespace.yaml | 17 + .../hdmf_common/v1_2_1/hdmf-common.base.yaml | 46 ++ .../v1_2_1/hdmf-common.nwb.language.yaml | 109 +++++ .../v1_2_1/hdmf-common.sparse.yaml | 77 +++ .../hdmf_common/v1_2_1/hdmf-common.table.yaml | 181 +++++++ .../linkml/hdmf_common/v1_2_1/namespace.yaml | 17 + .../hdmf_common/v1_3_0/hdmf-common.base.yaml | 46 ++ .../v1_3_0/hdmf-common.nwb.language.yaml | 109 +++++ .../v1_3_0/hdmf-common.resources.yaml | 158 ++++++ .../v1_3_0/hdmf-common.sparse.yaml | 66 +++ .../hdmf_common/v1_3_0/hdmf-common.table.yaml | 181 +++++++ .../linkml/hdmf_common/v1_3_0/namespace.yaml | 18 + .../hdmf_common/v1_4_0/hdmf-common.base.yaml | 46 ++ .../v1_4_0/hdmf-common.nwb.language.yaml | 109 +++++ .../v1_4_0/hdmf-common.sparse.yaml | 66 +++ .../hdmf_common/v1_4_0/hdmf-common.table.yaml | 166 +++++++ .../linkml/hdmf_common/v1_4_0/namespace.yaml | 17 + .../v1_5_0/hdmf-common.nwb.language.yaml | 2 +- .../hdmf_common/v1_5_1/hdmf-common.base.yaml | 46 ++ .../v1_5_1/hdmf-common.nwb.language.yaml | 109 +++++ .../v1_5_1/hdmf-common.sparse.yaml | 66 +++ .../hdmf_common/v1_5_1/hdmf-common.table.yaml | 185 +++++++ .../linkml/hdmf_common/v1_5_1/namespace.yaml | 17 + .../hdmf_common/v1_6_0/hdmf-common.base.yaml | 46 ++ .../v1_6_0/hdmf-common.nwb.language.yaml | 109 +++++ .../v1_6_0/hdmf-common.sparse.yaml | 66 +++ .../hdmf_common/v1_6_0/hdmf-common.table.yaml | 185 +++++++ .../linkml/hdmf_common/v1_6_0/namespace.yaml | 17 + .../hdmf_common/v1_7_0/hdmf-common.base.yaml | 46 ++ .../v1_7_0/hdmf-common.nwb.language.yaml | 109 +++++ .../v1_7_0/hdmf-common.sparse.yaml | 66 +++ .../hdmf_common/v1_7_0/hdmf-common.table.yaml | 185 +++++++ .../linkml/hdmf_common/v1_7_0/namespace.yaml | 17 + .../v1_8_0/hdmf-common.nwb.language.yaml | 2 +- .../hdmf-experimental.experimental.yaml | 2 +- .../hdmf-experimental.nwb.language.yaml | 2 +- .../v0_1_0/hdmf-experimental.resources.yaml | 2 +- .../hdmf-experimental.experimental.yaml | 31 ++ .../hdmf-experimental.nwb.language.yaml | 109 +++++ .../v0_2_0/hdmf-experimental.resources.yaml | 196 ++++++++ .../hdmf_experimental/v0_2_0/namespace.yaml | 17 + .../hdmf-experimental.experimental.yaml | 31 ++ .../hdmf-experimental.nwb.language.yaml | 109 +++++ .../v0_3_0/hdmf-experimental.resources.yaml | 199 ++++++++ .../hdmf_experimental/v0_3_0/namespace.yaml | 17 + .../hdmf-experimental.experimental.yaml | 31 ++ .../hdmf-experimental.nwb.language.yaml | 109 +++++ .../v0_4_0/hdmf-experimental.resources.yaml | 222 +++++++++ .../hdmf_experimental/v0_4_0/namespace.yaml | 17 + .../hdmf-experimental.nwb.language.yaml | 2 +- nwb_linkml/tests/fixtures.py | 8 +- nwb_linkml/tests/test_includes/test_hdmf.py | 3 +- 118 files changed, 12050 insertions(+), 84 deletions(-) create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/__init__.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/hdmf_common_base.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/hdmf_common_sparse.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/hdmf_common_table.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/namespace.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/__init__.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/hdmf_common_base.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/hdmf_common_sparse.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/hdmf_common_table.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/namespace.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/__init__.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_base.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_resources.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_sparse.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_table.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/namespace.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/__init__.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/hdmf_common_base.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/hdmf_common_sparse.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/hdmf_common_table.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/namespace.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/__init__.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/hdmf_common_base.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/hdmf_common_sparse.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/hdmf_common_table.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/namespace.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/__init__.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/hdmf_common_base.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/hdmf_common_sparse.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/hdmf_common_table.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/namespace.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/__init__.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/hdmf_common_base.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/hdmf_common_sparse.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/hdmf_common_table.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/namespace.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/__init__.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_experimental.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_resources.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/namespace.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/__init__.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_experimental.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_resources.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/namespace.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/__init__.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_experimental.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_resources.py create mode 100644 nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/namespace.py create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.base.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.nwb.language.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.sparse.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.table.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/namespace.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.base.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.nwb.language.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.sparse.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.table.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/namespace.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.base.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.nwb.language.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.resources.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.sparse.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.table.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/namespace.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.base.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.nwb.language.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.sparse.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.table.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/namespace.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.base.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.nwb.language.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.sparse.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.table.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/namespace.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.base.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.nwb.language.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.sparse.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.table.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/namespace.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.base.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.nwb.language.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.sparse.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.table.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/namespace.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.experimental.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.nwb.language.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.resources.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/namespace.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.experimental.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.nwb.language.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.resources.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/namespace.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.experimental.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.nwb.language.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.resources.yaml create mode 100644 nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/namespace.yaml diff --git a/nwb_linkml/src/nwb_linkml/generators/pydantic.py b/nwb_linkml/src/nwb_linkml/generators/pydantic.py index 3ecf605..0cc613d 100644 --- a/nwb_linkml/src/nwb_linkml/generators/pydantic.py +++ b/nwb_linkml/src/nwb_linkml/generators/pydantic.py @@ -234,6 +234,7 @@ class AfterGenerateSlot: slot.imports = NamedImports return slot + class AfterGenerateClass: """ Container class for class-modification methods diff --git a/nwb_linkml/src/nwb_linkml/includes/hdmf.py b/nwb_linkml/src/nwb_linkml/includes/hdmf.py index fdbd355..c86499b 100644 --- a/nwb_linkml/src/nwb_linkml/includes/hdmf.py +++ b/nwb_linkml/src/nwb_linkml/includes/hdmf.py @@ -1,6 +1,7 @@ """ Special types for mimicking HDMF special case behavior """ + from typing import Any, ClassVar, Dict, List, Optional, Union, Tuple, overload, TYPE_CHECKING @@ -23,16 +24,18 @@ class DynamicTableMixin(BaseModel): model_config = ConfigDict(extra="allow") __pydantic_extra__: Dict[str, Union[list, "NDArray", "VectorData"]] - NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ("name", "colnames", "description",) + NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( + "name", + "colnames", + "description", + ) # overridden by subclass but implemented here for testing and typechecking purposes :) colnames: List[str] = Field(default_factory=list) @property def _columns(self) -> Dict[str, Union[list, "NDArray", "VectorData"]]: - return { - k: getattr(self, k) for i, k in enumerate(self.colnames) - } + return {k: getattr(self, k) for i, k in enumerate(self.colnames)} @property def _columns_list(self) -> List[Union[list, "NDArray", "VectorData"]]: @@ -48,12 +51,26 @@ class DynamicTableMixin(BaseModel): def __getitem__(self, item: Tuple[int, Union[int, str]]) -> Any: ... @overload - def __getitem__(self, item: Tuple[Union[int,slice], ...]) -> Union[DataFrame, list, "NDArray", "VectorData",]: ... + def __getitem__(self, item: Tuple[Union[int, slice], ...]) -> Union[ + DataFrame, + list, + "NDArray", + "VectorData", + ]: ... @overload def __getitem__(self, item: slice) -> DataFrame: ... - def __getitem__(self, item: Union[str, int, slice, Tuple[int, Union[int, str]], Tuple[Union[int, slice], ...],]) -> Any: + def __getitem__( + self, + item: Union[ + str, + int, + slice, + Tuple[int, Union[int, str]], + Tuple[Union[int, slice], ...], + ], + ) -> Any: """ Get an item from the table @@ -74,7 +91,9 @@ class DynamicTableMixin(BaseModel): elif isinstance(item, tuple): if len(item) != 2: raise ValueError( - f"DynamicTables are 2-dimensional, can't index with more than 2 indices like {item}") + "DynamicTables are 2-dimensional, can't index with more than 2 indices like" + f" {item}" + ) # all other cases are tuples of (rows, cols) rows, cols = item @@ -85,20 +104,17 @@ class DynamicTableMixin(BaseModel): else: raise ValueError(f"Unsure how to get item with key {item}") - - def _slice_range(self, rows: Union[int, slice], cols: Optional[Union[str, List[str]]] = None) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + def _slice_range( + self, rows: Union[int, slice], cols: Optional[Union[str, List[str]]] = None + ) -> Dict[str, Union[list, "NDArray", "VectorData"]]: if cols is None: cols = self.colnames elif isinstance(cols, str): cols = [cols] - data = { - k: self._columns[k][rows] for k in cols - } + data = {k: self._columns[k][rows] for k in cols} return data - - def __setitem__(self, key: str, value: Any) -> None: raise NotImplementedError("TODO") @@ -107,10 +123,10 @@ class DynamicTableMixin(BaseModel): Add a column, appending it to ``colnames`` """ # don't use this while building the model - if not getattr(self, '__pydantic_complete__', False): + if not getattr(self, "__pydantic_complete__", False): return super().__setattr__(key, value) - if key not in self.model_fields_set and not key.endswith('_index'): + if key not in self.model_fields_set and not key.endswith("_index"): self.colnames.append(key) return super().__setattr__(key, value) @@ -124,19 +140,23 @@ class DynamicTableMixin(BaseModel): the model dict is ordered after python3.6, so we can use that minus anything in :attr:`.NON_COLUMN_FIELDS` to determine order implied from passage order """ - if 'colnames' not in model: - colnames = [k for k in model.keys() - if k not in cls.NON_COLUMN_FIELDS - and not k.endswith('_index')] - model['colnames'] = colnames + if "colnames" not in model: + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") + ] + model["colnames"] = colnames else: # add any columns not explicitly given an order at the end - colnames = [k for k in model.keys() if - k not in cls.NON_COLUMN_FIELDS - and not k.endswith('_index') - and k not in model['colnames'].keys() - ] - model['colnames'].extend(colnames) + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS + and not k.endswith("_index") + and k not in model["colnames"].keys() + ] + model["colnames"].extend(colnames) return model @model_validator(mode="after") @@ -164,12 +184,11 @@ class DynamicTableMixin(BaseModel): return self - - class VectorDataMixin(BaseModel): """ Mixin class to give VectorData indexing abilities """ + _index: Optional["VectorIndex"] = None # redefined in `VectorData`, but included here for testing and type checking @@ -194,6 +213,7 @@ class VectorIndexMixin(BaseModel): """ Mixin class to give VectorIndex indexing abilities """ + # redefined in `VectorData`, but included here for testing and type checking array: Optional[NDArray] = None target: Optional["VectorData"] = None @@ -219,7 +239,6 @@ class VectorIndexMixin(BaseModel): else: raise NotImplementedError("DynamicTableRange not supported yet") - def __setitem__(self, key, value) -> None: if self._index: # VectorIndex is the thing that knows how to do the slicing @@ -229,11 +248,18 @@ class VectorIndexMixin(BaseModel): DYNAMIC_TABLE_IMPORTS = Imports( - imports = [ + imports=[ Import(module="pandas", objects=[ObjectImport(name="DataFrame")]), - Import(module="typing", objects=[ObjectImport(name="ClassVar"), ObjectImport(name="overload"), ObjectImport(name="Tuple")]), - Import(module='numpydantic', objects=[ObjectImport(name='NDArray')]), - Import(module="pydantic", objects=[ObjectImport(name="model_validator")]) + Import( + module="typing", + objects=[ + ObjectImport(name="ClassVar"), + ObjectImport(name="overload"), + ObjectImport(name="Tuple"), + ], + ), + Import(module="numpydantic", objects=[ObjectImport(name="NDArray")]), + Import(module="pydantic", objects=[ObjectImport(name="model_validator")]), ] ) """ diff --git a/nwb_linkml/src/nwb_linkml/io/schema.py b/nwb_linkml/src/nwb_linkml/io/schema.py index d5ce5c8..a162856 100644 --- a/nwb_linkml/src/nwb_linkml/io/schema.py +++ b/nwb_linkml/src/nwb_linkml/io/schema.py @@ -120,7 +120,9 @@ def load_namespace_adapter( return adapter -def load_nwb_core(core_version: str = "2.7.0", hdmf_version: str = "1.8.0", hdmf_only:bool=False) -> NamespacesAdapter: +def load_nwb_core( + core_version: str = "2.7.0", hdmf_version: str = "1.8.0", hdmf_only: bool = False +) -> NamespacesAdapter: """ Convenience function for loading the NWB core schema + hdmf-common as a namespace adapter. diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_1_0/hdmf_common_table.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_1_0/hdmf_common_table.py index ffd4424..c6e8f35 100644 --- a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_1_0/hdmf_common_table.py +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_1_0/hdmf_common_table.py @@ -4,10 +4,11 @@ from decimal import Decimal from enum import Enum import re import sys -from typing import Any, ClassVar, List, Literal, Dict, Optional, Union -from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator import numpy as np +from pandas import DataFrame +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union, overload, Tuple from numpydantic import NDArray, Shape +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator, model_validator metamodel_version = "None" version = "1.1.0" @@ -46,6 +47,241 @@ class LinkMLMeta(RootModel): NUMPYDANTIC_VERSION = "1.2.1" + + +class VectorDataMixin(BaseModel): + """ + Mixin class to give VectorData indexing abilities + """ + + _index: Optional["VectorIndex"] = None + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + + def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + return self._index[item] + else: + return self.array[item] + + def __setitem__(self, key, value) -> None: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class VectorIndexMixin(BaseModel): + """ + Mixin class to give VectorIndex indexing abilities + """ + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + target: Optional["VectorData"] = None + + def _getitem_helper(self, arg: int): + """ + Mimicking :func:`hdmf.common.table.VectorIndex.__getitem_helper` + """ + + start = 0 if arg == 0 else self.array[arg - 1] + end = self.array[arg] + return self.target.array[slice(start, end)] + + def __getitem__(self, item: Union[int, slice]) -> Any: + if self.target is None: + return self.array[item] + elif type(self.target).__name__ == "VectorData": + if isinstance(item, int): + return self._getitem_helper(item) + else: + idx = range(*item.indices(len(self.array))) + return [self._getitem_helper(i) for i in idx] + else: + raise NotImplementedError("DynamicTableRange not supported yet") + + def __setitem__(self, key, value) -> None: + if self._index: + # VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class DynamicTableMixin(BaseModel): + """ + Mixin to make DynamicTable subclasses behave like tables/dataframes + + Mimicking some of the behavior from :class:`hdmf.common.table.DynamicTable` + but simplifying along the way :) + """ + + model_config = ConfigDict(extra="allow") + __pydantic_extra__: Dict[str, Union[list, "NDArray", "VectorData"]] + NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( + "name", + "colnames", + "description", + ) + + # overridden by subclass but implemented here for testing and typechecking purposes :) + colnames: List[str] = Field(default_factory=list) + + @property + def _columns(self) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + return {k: getattr(self, k) for i, k in enumerate(self.colnames)} + + @property + def _columns_list(self) -> List[Union[list, "NDArray", "VectorData"]]: + return [getattr(self, k) for i, k in enumerate(self.colnames)] + + @overload + def __getitem__(self, item: str) -> Union[list, "NDArray", "VectorData"]: ... + + @overload + def __getitem__(self, item: int) -> DataFrame: ... + + @overload + def __getitem__(self, item: Tuple[int, Union[int, str]]) -> Any: ... + + @overload + def __getitem__(self, item: Tuple[Union[int, slice], ...]) -> Union[ + DataFrame, + list, + "NDArray", + "VectorData", + ]: ... + + @overload + def __getitem__(self, item: slice) -> DataFrame: ... + + def __getitem__( + self, + item: Union[ + str, + int, + slice, + Tuple[int, Union[int, str]], + Tuple[Union[int, slice], ...], + ], + ) -> Any: + """ + Get an item from the table + + If item is... + + - ``str`` : get the column with this name + - ``int`` : get the row at this index + - ``tuple[int, int]`` : get a specific cell value eg. (0,1) gets the 0th row and 1st column + - ``tuple[int, str]`` : get a specific cell value eg. (0, 'colname') + gets the 0th row from ``colname`` + - ``tuple[int | slice, int | slice]`` : get a range of cells from a range of columns. + returns as a :class:`pandas.DataFrame` + """ + if isinstance(item, str): + return self._columns[item] + if isinstance(item, (int, slice)): + return DataFrame.from_dict(self._slice_range(item)) + elif isinstance(item, tuple): + if len(item) != 2: + raise ValueError( + "DynamicTables are 2-dimensional, can't index with more than 2 indices like" + f" {item}" + ) + + # all other cases are tuples of (rows, cols) + rows, cols = item + if isinstance(cols, (int, slice)): + cols = self.colnames[cols] + data = self._slice_range(rows, cols) + return DataFrame.from_dict(data) + else: + raise ValueError(f"Unsure how to get item with key {item}") + + def _slice_range( + self, rows: Union[int, slice], cols: Optional[Union[str, List[str]]] = None + ) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + if cols is None: + cols = self.colnames + elif isinstance(cols, str): + cols = [cols] + + data = {k: self._columns[k][rows] for k in cols} + return data + + def __setitem__(self, key: str, value: Any) -> None: + raise NotImplementedError("TODO") + + def __setattr__(self, key: str, value: Union[list, "NDArray", "VectorData"]): + """ + Add a column, appending it to ``colnames`` + """ + # don't use this while building the model + if not getattr(self, "__pydantic_complete__", False): + return super().__setattr__(key, value) + + if key not in self.model_fields_set and not key.endswith("_index"): + self.colnames.append(key) + + return super().__setattr__(key, value) + + @model_validator(mode="before") + @classmethod + def create_colnames(cls, model: Dict[str, Any]): + """ + Construct colnames from arguments. + + the model dict is ordered after python3.6, so we can use that minus + anything in :attr:`.NON_COLUMN_FIELDS` to determine order implied from passage order + """ + if "colnames" not in model: + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") + ] + model["colnames"] = colnames + else: + # add any columns not explicitly given an order at the end + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS + and not k.endswith("_index") + and k not in model["colnames"].keys() + ] + model["colnames"].extend(colnames) + return model + + @model_validator(mode="after") + def resolve_targets(self) -> "DynamicTableMixin": + """ + Ensure that any implicitly indexed columns are linked, and create backlinks + """ + for key, col in self._columns.items(): + if isinstance(col, VectorData): + # find an index + idx = None + for field_name in self.model_fields_set: + # implicit name-based index + field = getattr(self, field_name) + if isinstance(field, VectorIndex): + if field_name == f"{key}_index": + idx = field + break + elif field.target is col: + idx = field + break + if idx is not None: + col._index = idx + idx.target = col + return self + + linkml_meta = LinkMLMeta( { "annotations": { @@ -87,7 +323,7 @@ class Index(Data): ) -class VectorData(Data): +class VectorData(VectorDataMixin): """ An n-dimensional dataset representing a column of a DynamicTable. If used without an accompanying VectorIndex, first dimension is along the rows of the DynamicTable and each step along the first dimension is a cell of the larger table. VectorData can also be used to represent a ragged array if paired with a VectorIndex. This allows for storing arrays of varying length in a single cell of the DynamicTable by indexing into this VectorData. The first vector is at VectorData[0:VectorIndex(0)+1]. The second vector is at VectorData[VectorIndex(0)+1:VectorIndex(1)+1], and so on. """ @@ -102,7 +338,7 @@ class VectorData(Data): ) -class VectorIndex(Index): +class VectorIndex(VectorIndexMixin): """ Used with VectorData to encode a ragged array. An array of indices into the first dimension of the target VectorData, and forming a map between the rows of a DynamicTable and the indices of the VectorData. """ @@ -161,7 +397,7 @@ class Container(ConfiguredBaseModel): name: str = Field(...) -class DynamicTable(Container): +class DynamicTable(DynamicTableMixin): """ A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). Apart from a column that contains unique identifiers for each row there are no other required datasets. Users are free to add any number of VectorData objects here. Table functionality is already supported through compound types, which is analogous to storing an array-of-structs. DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable. For example, DynamicTable was originally developed for storing trial data and spike unit metadata. Both of these use cases are expected to produce relatively small tables, so the spatial locality of multiple datasets present in a DynamicTable is not expected to have a significant performance impact. Additionally, requirements of trial and unit metadata tables are sufficiently diverse that performance implications can be overlooked in favor of usability. """ diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_1_2/hdmf_common_table.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_1_2/hdmf_common_table.py index 0b75bec..44209ba 100644 --- a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_1_2/hdmf_common_table.py +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_1_2/hdmf_common_table.py @@ -4,10 +4,11 @@ from decimal import Decimal from enum import Enum import re import sys -from typing import Any, ClassVar, List, Literal, Dict, Optional, Union -from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator import numpy as np +from pandas import DataFrame +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union, overload, Tuple from numpydantic import NDArray, Shape +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator, model_validator metamodel_version = "None" version = "1.1.2" @@ -46,6 +47,241 @@ class LinkMLMeta(RootModel): NUMPYDANTIC_VERSION = "1.2.1" + + +class VectorDataMixin(BaseModel): + """ + Mixin class to give VectorData indexing abilities + """ + + _index: Optional["VectorIndex"] = None + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + + def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + return self._index[item] + else: + return self.array[item] + + def __setitem__(self, key, value) -> None: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class VectorIndexMixin(BaseModel): + """ + Mixin class to give VectorIndex indexing abilities + """ + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + target: Optional["VectorData"] = None + + def _getitem_helper(self, arg: int): + """ + Mimicking :func:`hdmf.common.table.VectorIndex.__getitem_helper` + """ + + start = 0 if arg == 0 else self.array[arg - 1] + end = self.array[arg] + return self.target.array[slice(start, end)] + + def __getitem__(self, item: Union[int, slice]) -> Any: + if self.target is None: + return self.array[item] + elif type(self.target).__name__ == "VectorData": + if isinstance(item, int): + return self._getitem_helper(item) + else: + idx = range(*item.indices(len(self.array))) + return [self._getitem_helper(i) for i in idx] + else: + raise NotImplementedError("DynamicTableRange not supported yet") + + def __setitem__(self, key, value) -> None: + if self._index: + # VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class DynamicTableMixin(BaseModel): + """ + Mixin to make DynamicTable subclasses behave like tables/dataframes + + Mimicking some of the behavior from :class:`hdmf.common.table.DynamicTable` + but simplifying along the way :) + """ + + model_config = ConfigDict(extra="allow") + __pydantic_extra__: Dict[str, Union[list, "NDArray", "VectorData"]] + NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( + "name", + "colnames", + "description", + ) + + # overridden by subclass but implemented here for testing and typechecking purposes :) + colnames: List[str] = Field(default_factory=list) + + @property + def _columns(self) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + return {k: getattr(self, k) for i, k in enumerate(self.colnames)} + + @property + def _columns_list(self) -> List[Union[list, "NDArray", "VectorData"]]: + return [getattr(self, k) for i, k in enumerate(self.colnames)] + + @overload + def __getitem__(self, item: str) -> Union[list, "NDArray", "VectorData"]: ... + + @overload + def __getitem__(self, item: int) -> DataFrame: ... + + @overload + def __getitem__(self, item: Tuple[int, Union[int, str]]) -> Any: ... + + @overload + def __getitem__(self, item: Tuple[Union[int, slice], ...]) -> Union[ + DataFrame, + list, + "NDArray", + "VectorData", + ]: ... + + @overload + def __getitem__(self, item: slice) -> DataFrame: ... + + def __getitem__( + self, + item: Union[ + str, + int, + slice, + Tuple[int, Union[int, str]], + Tuple[Union[int, slice], ...], + ], + ) -> Any: + """ + Get an item from the table + + If item is... + + - ``str`` : get the column with this name + - ``int`` : get the row at this index + - ``tuple[int, int]`` : get a specific cell value eg. (0,1) gets the 0th row and 1st column + - ``tuple[int, str]`` : get a specific cell value eg. (0, 'colname') + gets the 0th row from ``colname`` + - ``tuple[int | slice, int | slice]`` : get a range of cells from a range of columns. + returns as a :class:`pandas.DataFrame` + """ + if isinstance(item, str): + return self._columns[item] + if isinstance(item, (int, slice)): + return DataFrame.from_dict(self._slice_range(item)) + elif isinstance(item, tuple): + if len(item) != 2: + raise ValueError( + "DynamicTables are 2-dimensional, can't index with more than 2 indices like" + f" {item}" + ) + + # all other cases are tuples of (rows, cols) + rows, cols = item + if isinstance(cols, (int, slice)): + cols = self.colnames[cols] + data = self._slice_range(rows, cols) + return DataFrame.from_dict(data) + else: + raise ValueError(f"Unsure how to get item with key {item}") + + def _slice_range( + self, rows: Union[int, slice], cols: Optional[Union[str, List[str]]] = None + ) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + if cols is None: + cols = self.colnames + elif isinstance(cols, str): + cols = [cols] + + data = {k: self._columns[k][rows] for k in cols} + return data + + def __setitem__(self, key: str, value: Any) -> None: + raise NotImplementedError("TODO") + + def __setattr__(self, key: str, value: Union[list, "NDArray", "VectorData"]): + """ + Add a column, appending it to ``colnames`` + """ + # don't use this while building the model + if not getattr(self, "__pydantic_complete__", False): + return super().__setattr__(key, value) + + if key not in self.model_fields_set and not key.endswith("_index"): + self.colnames.append(key) + + return super().__setattr__(key, value) + + @model_validator(mode="before") + @classmethod + def create_colnames(cls, model: Dict[str, Any]): + """ + Construct colnames from arguments. + + the model dict is ordered after python3.6, so we can use that minus + anything in :attr:`.NON_COLUMN_FIELDS` to determine order implied from passage order + """ + if "colnames" not in model: + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") + ] + model["colnames"] = colnames + else: + # add any columns not explicitly given an order at the end + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS + and not k.endswith("_index") + and k not in model["colnames"].keys() + ] + model["colnames"].extend(colnames) + return model + + @model_validator(mode="after") + def resolve_targets(self) -> "DynamicTableMixin": + """ + Ensure that any implicitly indexed columns are linked, and create backlinks + """ + for key, col in self._columns.items(): + if isinstance(col, VectorData): + # find an index + idx = None + for field_name in self.model_fields_set: + # implicit name-based index + field = getattr(self, field_name) + if isinstance(field, VectorIndex): + if field_name == f"{key}_index": + idx = field + break + elif field.target is col: + idx = field + break + if idx is not None: + col._index = idx + idx.target = col + return self + + linkml_meta = LinkMLMeta( { "annotations": { @@ -87,7 +323,7 @@ class Index(Data): ) -class VectorData(Data): +class VectorData(VectorDataMixin): """ An n-dimensional dataset representing a column of a DynamicTable. If used without an accompanying VectorIndex, first dimension is along the rows of the DynamicTable and each step along the first dimension is a cell of the larger table. VectorData can also be used to represent a ragged array if paired with a VectorIndex. This allows for storing arrays of varying length in a single cell of the DynamicTable by indexing into this VectorData. The first vector is at VectorData[0:VectorIndex(0)+1]. The second vector is at VectorData[VectorIndex(0)+1:VectorIndex(1)+1], and so on. """ @@ -102,7 +338,7 @@ class VectorData(Data): ) -class VectorIndex(Index): +class VectorIndex(VectorIndexMixin): """ Used with VectorData to encode a ragged array. An array of indices into the first dimension of the target VectorData, and forming a map between the rows of a DynamicTable and the indices of the VectorData. """ @@ -161,7 +397,7 @@ class Container(ConfiguredBaseModel): name: str = Field(...) -class DynamicTable(Container): +class DynamicTable(DynamicTableMixin): """ A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). Apart from a column that contains unique identifiers for each row there are no other required datasets. Users are free to add any number of VectorData objects here. Table functionality is already supported through compound types, which is analogous to storing an array-of-structs. DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable. For example, DynamicTable was originally developed for storing trial data and spike unit metadata. Both of these use cases are expected to produce relatively small tables, so the spatial locality of multiple datasets present in a DynamicTable is not expected to have a significant performance impact. Additionally, requirements of trial and unit metadata tables are sufficiently diverse that performance implications can be overlooked in favor of usability. """ diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_1_3/hdmf_common_table.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_1_3/hdmf_common_table.py index ae84bd1..703ff6c 100644 --- a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_1_3/hdmf_common_table.py +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_1_3/hdmf_common_table.py @@ -4,10 +4,11 @@ from decimal import Decimal from enum import Enum import re import sys -from typing import Any, ClassVar, List, Literal, Dict, Optional, Union -from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator import numpy as np +from pandas import DataFrame +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union, overload, Tuple from numpydantic import NDArray, Shape +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator, model_validator metamodel_version = "None" version = "1.1.3" @@ -46,6 +47,241 @@ class LinkMLMeta(RootModel): NUMPYDANTIC_VERSION = "1.2.1" + + +class VectorDataMixin(BaseModel): + """ + Mixin class to give VectorData indexing abilities + """ + + _index: Optional["VectorIndex"] = None + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + + def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + return self._index[item] + else: + return self.array[item] + + def __setitem__(self, key, value) -> None: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class VectorIndexMixin(BaseModel): + """ + Mixin class to give VectorIndex indexing abilities + """ + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + target: Optional["VectorData"] = None + + def _getitem_helper(self, arg: int): + """ + Mimicking :func:`hdmf.common.table.VectorIndex.__getitem_helper` + """ + + start = 0 if arg == 0 else self.array[arg - 1] + end = self.array[arg] + return self.target.array[slice(start, end)] + + def __getitem__(self, item: Union[int, slice]) -> Any: + if self.target is None: + return self.array[item] + elif type(self.target).__name__ == "VectorData": + if isinstance(item, int): + return self._getitem_helper(item) + else: + idx = range(*item.indices(len(self.array))) + return [self._getitem_helper(i) for i in idx] + else: + raise NotImplementedError("DynamicTableRange not supported yet") + + def __setitem__(self, key, value) -> None: + if self._index: + # VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class DynamicTableMixin(BaseModel): + """ + Mixin to make DynamicTable subclasses behave like tables/dataframes + + Mimicking some of the behavior from :class:`hdmf.common.table.DynamicTable` + but simplifying along the way :) + """ + + model_config = ConfigDict(extra="allow") + __pydantic_extra__: Dict[str, Union[list, "NDArray", "VectorData"]] + NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( + "name", + "colnames", + "description", + ) + + # overridden by subclass but implemented here for testing and typechecking purposes :) + colnames: List[str] = Field(default_factory=list) + + @property + def _columns(self) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + return {k: getattr(self, k) for i, k in enumerate(self.colnames)} + + @property + def _columns_list(self) -> List[Union[list, "NDArray", "VectorData"]]: + return [getattr(self, k) for i, k in enumerate(self.colnames)] + + @overload + def __getitem__(self, item: str) -> Union[list, "NDArray", "VectorData"]: ... + + @overload + def __getitem__(self, item: int) -> DataFrame: ... + + @overload + def __getitem__(self, item: Tuple[int, Union[int, str]]) -> Any: ... + + @overload + def __getitem__(self, item: Tuple[Union[int, slice], ...]) -> Union[ + DataFrame, + list, + "NDArray", + "VectorData", + ]: ... + + @overload + def __getitem__(self, item: slice) -> DataFrame: ... + + def __getitem__( + self, + item: Union[ + str, + int, + slice, + Tuple[int, Union[int, str]], + Tuple[Union[int, slice], ...], + ], + ) -> Any: + """ + Get an item from the table + + If item is... + + - ``str`` : get the column with this name + - ``int`` : get the row at this index + - ``tuple[int, int]`` : get a specific cell value eg. (0,1) gets the 0th row and 1st column + - ``tuple[int, str]`` : get a specific cell value eg. (0, 'colname') + gets the 0th row from ``colname`` + - ``tuple[int | slice, int | slice]`` : get a range of cells from a range of columns. + returns as a :class:`pandas.DataFrame` + """ + if isinstance(item, str): + return self._columns[item] + if isinstance(item, (int, slice)): + return DataFrame.from_dict(self._slice_range(item)) + elif isinstance(item, tuple): + if len(item) != 2: + raise ValueError( + "DynamicTables are 2-dimensional, can't index with more than 2 indices like" + f" {item}" + ) + + # all other cases are tuples of (rows, cols) + rows, cols = item + if isinstance(cols, (int, slice)): + cols = self.colnames[cols] + data = self._slice_range(rows, cols) + return DataFrame.from_dict(data) + else: + raise ValueError(f"Unsure how to get item with key {item}") + + def _slice_range( + self, rows: Union[int, slice], cols: Optional[Union[str, List[str]]] = None + ) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + if cols is None: + cols = self.colnames + elif isinstance(cols, str): + cols = [cols] + + data = {k: self._columns[k][rows] for k in cols} + return data + + def __setitem__(self, key: str, value: Any) -> None: + raise NotImplementedError("TODO") + + def __setattr__(self, key: str, value: Union[list, "NDArray", "VectorData"]): + """ + Add a column, appending it to ``colnames`` + """ + # don't use this while building the model + if not getattr(self, "__pydantic_complete__", False): + return super().__setattr__(key, value) + + if key not in self.model_fields_set and not key.endswith("_index"): + self.colnames.append(key) + + return super().__setattr__(key, value) + + @model_validator(mode="before") + @classmethod + def create_colnames(cls, model: Dict[str, Any]): + """ + Construct colnames from arguments. + + the model dict is ordered after python3.6, so we can use that minus + anything in :attr:`.NON_COLUMN_FIELDS` to determine order implied from passage order + """ + if "colnames" not in model: + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") + ] + model["colnames"] = colnames + else: + # add any columns not explicitly given an order at the end + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS + and not k.endswith("_index") + and k not in model["colnames"].keys() + ] + model["colnames"].extend(colnames) + return model + + @model_validator(mode="after") + def resolve_targets(self) -> "DynamicTableMixin": + """ + Ensure that any implicitly indexed columns are linked, and create backlinks + """ + for key, col in self._columns.items(): + if isinstance(col, VectorData): + # find an index + idx = None + for field_name in self.model_fields_set: + # implicit name-based index + field = getattr(self, field_name) + if isinstance(field, VectorIndex): + if field_name == f"{key}_index": + idx = field + break + elif field.target is col: + idx = field + break + if idx is not None: + col._index = idx + idx.target = col + return self + + linkml_meta = LinkMLMeta( { "annotations": { @@ -87,7 +323,7 @@ class Index(Data): ) -class VectorData(Data): +class VectorData(VectorDataMixin): """ An n-dimensional dataset representing a column of a DynamicTable. If used without an accompanying VectorIndex, first dimension is along the rows of the DynamicTable and each step along the first dimension is a cell of the larger table. VectorData can also be used to represent a ragged array if paired with a VectorIndex. This allows for storing arrays of varying length in a single cell of the DynamicTable by indexing into this VectorData. The first vector is at VectorData[0:VectorIndex(0)+1]. The second vector is at VectorData[VectorIndex(0)+1:VectorIndex(1)+1], and so on. """ @@ -110,7 +346,7 @@ class VectorData(Data): ] = Field(None) -class VectorIndex(Index): +class VectorIndex(VectorIndexMixin): """ Used with VectorData to encode a ragged array. An array of indices into the first dimension of the target VectorData, and forming a map between the rows of a DynamicTable and the indices of the VectorData. """ @@ -180,7 +416,7 @@ class Container(ConfiguredBaseModel): name: str = Field(...) -class DynamicTable(Container): +class DynamicTable(DynamicTableMixin): """ A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). Apart from a column that contains unique identifiers for each row there are no other required datasets. Users are free to add any number of VectorData objects here. Table functionality is already supported through compound types, which is analogous to storing an array-of-structs. DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable. For example, DynamicTable was originally developed for storing trial data and spike unit metadata. Both of these use cases are expected to produce relatively small tables, so the spatial locality of multiple datasets present in a DynamicTable is not expected to have a significant performance impact. Additionally, requirements of trial and unit metadata tables are sufficiently diverse that performance implications can be overlooked in favor of usability. """ diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/__init__.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/__init__.py @@ -0,0 +1 @@ + diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/hdmf_common_base.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/hdmf_common_base.py new file mode 100644 index 0000000..1d657d9 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/hdmf_common_base.py @@ -0,0 +1,88 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np + +metamodel_version = "None" +version = "1.2.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.base/", + "id": "hdmf-common.base", + "imports": ["hdmf-common.nwb.language"], + "name": "hdmf-common.base", + } +) + + +class Data(ConfiguredBaseModel): + """ + An abstract data type for a dataset. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + name: str = Field(...) + + +class Container(ConfiguredBaseModel): + """ + An abstract data type for a group storing collections of data and metadata. Base type for all data and metadata containers. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + name: str = Field(...) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +Data.model_rebuild() +Container.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/hdmf_common_sparse.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/hdmf_common_sparse.py new file mode 100644 index 0000000..31afdb0 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/hdmf_common_sparse.py @@ -0,0 +1,125 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np + +metamodel_version = "None" +version = "1.2.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.sparse/", + "id": "hdmf-common.sparse", + "imports": ["hdmf-common.nwb.language"], + "name": "hdmf-common.sparse", + } +) + + +class CSRMatrix(ConfiguredBaseModel): + """ + a compressed sparse row matrix + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.sparse", "tree_root": True} + ) + + name: str = Field(...) + shape: Optional[int] = Field(None, description="""the shape of this sparse matrix""") + indices: CSRMatrixIndices = Field(..., description="""column indices""") + indptr: CSRMatrixIndptr = Field(..., description="""index pointer""") + data: CSRMatrixData = Field(..., description="""values in the matrix""") + + +class CSRMatrixIndices(ConfiguredBaseModel): + """ + column indices + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) + + name: Literal["indices"] = Field( + "indices", + json_schema_extra={ + "linkml_meta": {"equals_string": "indices", "ifabsent": "string(indices)"} + }, + ) + + +class CSRMatrixIndptr(ConfiguredBaseModel): + """ + index pointer + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) + + name: Literal["indptr"] = Field( + "indptr", + json_schema_extra={ + "linkml_meta": {"equals_string": "indptr", "ifabsent": "string(indptr)"} + }, + ) + + +class CSRMatrixData(ConfiguredBaseModel): + """ + values in the matrix + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) + + name: Literal["data"] = Field( + "data", + json_schema_extra={"linkml_meta": {"equals_string": "data", "ifabsent": "string(data)"}}, + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +CSRMatrix.model_rebuild() +CSRMatrixIndices.model_rebuild() +CSRMatrixIndptr.model_rebuild() +CSRMatrixData.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/hdmf_common_table.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/hdmf_common_table.py new file mode 100644 index 0000000..6ded400 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/hdmf_common_table.py @@ -0,0 +1,449 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +import numpy as np +from ...hdmf_common.v1_2_0.hdmf_common_base import Data, Container +from pandas import DataFrame +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union, overload, Tuple +from numpydantic import NDArray, Shape +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator, model_validator + +metamodel_version = "None" +version = "1.2.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +NUMPYDANTIC_VERSION = "1.2.1" + + +class VectorDataMixin(BaseModel): + """ + Mixin class to give VectorData indexing abilities + """ + + _index: Optional["VectorIndex"] = None + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + + def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + return self._index[item] + else: + return self.array[item] + + def __setitem__(self, key, value) -> None: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class VectorIndexMixin(BaseModel): + """ + Mixin class to give VectorIndex indexing abilities + """ + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + target: Optional["VectorData"] = None + + def _getitem_helper(self, arg: int): + """ + Mimicking :func:`hdmf.common.table.VectorIndex.__getitem_helper` + """ + + start = 0 if arg == 0 else self.array[arg - 1] + end = self.array[arg] + return self.target.array[slice(start, end)] + + def __getitem__(self, item: Union[int, slice]) -> Any: + if self.target is None: + return self.array[item] + elif type(self.target).__name__ == "VectorData": + if isinstance(item, int): + return self._getitem_helper(item) + else: + idx = range(*item.indices(len(self.array))) + return [self._getitem_helper(i) for i in idx] + else: + raise NotImplementedError("DynamicTableRange not supported yet") + + def __setitem__(self, key, value) -> None: + if self._index: + # VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class DynamicTableMixin(BaseModel): + """ + Mixin to make DynamicTable subclasses behave like tables/dataframes + + Mimicking some of the behavior from :class:`hdmf.common.table.DynamicTable` + but simplifying along the way :) + """ + + model_config = ConfigDict(extra="allow") + __pydantic_extra__: Dict[str, Union[list, "NDArray", "VectorData"]] + NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( + "name", + "colnames", + "description", + ) + + # overridden by subclass but implemented here for testing and typechecking purposes :) + colnames: List[str] = Field(default_factory=list) + + @property + def _columns(self) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + return {k: getattr(self, k) for i, k in enumerate(self.colnames)} + + @property + def _columns_list(self) -> List[Union[list, "NDArray", "VectorData"]]: + return [getattr(self, k) for i, k in enumerate(self.colnames)] + + @overload + def __getitem__(self, item: str) -> Union[list, "NDArray", "VectorData"]: ... + + @overload + def __getitem__(self, item: int) -> DataFrame: ... + + @overload + def __getitem__(self, item: Tuple[int, Union[int, str]]) -> Any: ... + + @overload + def __getitem__(self, item: Tuple[Union[int, slice], ...]) -> Union[ + DataFrame, + list, + "NDArray", + "VectorData", + ]: ... + + @overload + def __getitem__(self, item: slice) -> DataFrame: ... + + def __getitem__( + self, + item: Union[ + str, + int, + slice, + Tuple[int, Union[int, str]], + Tuple[Union[int, slice], ...], + ], + ) -> Any: + """ + Get an item from the table + + If item is... + + - ``str`` : get the column with this name + - ``int`` : get the row at this index + - ``tuple[int, int]`` : get a specific cell value eg. (0,1) gets the 0th row and 1st column + - ``tuple[int, str]`` : get a specific cell value eg. (0, 'colname') + gets the 0th row from ``colname`` + - ``tuple[int | slice, int | slice]`` : get a range of cells from a range of columns. + returns as a :class:`pandas.DataFrame` + """ + if isinstance(item, str): + return self._columns[item] + if isinstance(item, (int, slice)): + return DataFrame.from_dict(self._slice_range(item)) + elif isinstance(item, tuple): + if len(item) != 2: + raise ValueError( + "DynamicTables are 2-dimensional, can't index with more than 2 indices like" + f" {item}" + ) + + # all other cases are tuples of (rows, cols) + rows, cols = item + if isinstance(cols, (int, slice)): + cols = self.colnames[cols] + data = self._slice_range(rows, cols) + return DataFrame.from_dict(data) + else: + raise ValueError(f"Unsure how to get item with key {item}") + + def _slice_range( + self, rows: Union[int, slice], cols: Optional[Union[str, List[str]]] = None + ) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + if cols is None: + cols = self.colnames + elif isinstance(cols, str): + cols = [cols] + + data = {k: self._columns[k][rows] for k in cols} + return data + + def __setitem__(self, key: str, value: Any) -> None: + raise NotImplementedError("TODO") + + def __setattr__(self, key: str, value: Union[list, "NDArray", "VectorData"]): + """ + Add a column, appending it to ``colnames`` + """ + # don't use this while building the model + if not getattr(self, "__pydantic_complete__", False): + return super().__setattr__(key, value) + + if key not in self.model_fields_set and not key.endswith("_index"): + self.colnames.append(key) + + return super().__setattr__(key, value) + + @model_validator(mode="before") + @classmethod + def create_colnames(cls, model: Dict[str, Any]): + """ + Construct colnames from arguments. + + the model dict is ordered after python3.6, so we can use that minus + anything in :attr:`.NON_COLUMN_FIELDS` to determine order implied from passage order + """ + if "colnames" not in model: + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") + ] + model["colnames"] = colnames + else: + # add any columns not explicitly given an order at the end + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS + and not k.endswith("_index") + and k not in model["colnames"].keys() + ] + model["colnames"].extend(colnames) + return model + + @model_validator(mode="after") + def resolve_targets(self) -> "DynamicTableMixin": + """ + Ensure that any implicitly indexed columns are linked, and create backlinks + """ + for key, col in self._columns.items(): + if isinstance(col, VectorData): + # find an index + idx = None + for field_name in self.model_fields_set: + # implicit name-based index + field = getattr(self, field_name) + if isinstance(field, VectorIndex): + if field_name == f"{key}_index": + idx = field + break + elif field.target is col: + idx = field + break + if idx is not None: + col._index = idx + idx.target = col + return self + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.table/", + "id": "hdmf-common.table", + "imports": ["hdmf-common.base", "hdmf-common.nwb.language"], + "name": "hdmf-common.table", + } +) + + +class VectorData(VectorDataMixin): + """ + An n-dimensional dataset representing a column of a DynamicTable. If used without an accompanying VectorIndex, first dimension is along the rows of the DynamicTable and each step along the first dimension is a cell of the larger table. VectorData can also be used to represent a ragged array if paired with a VectorIndex. This allows for storing arrays of varying length in a single cell of the DynamicTable by indexing into this VectorData. The first vector is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], and so on. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class VectorIndex(VectorIndexMixin): + """ + Used with VectorData to encode a ragged array. An array of indices into the first dimension of the target VectorData, and forming a map between the rows of a DynamicTable and the indices of the VectorData. The name of the VectorIndex is expected to be the name of the target VectorData object followed by \"_index\". + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + target: Optional[VectorData] = Field( + None, description="""Reference to the target dataset that this index applies to.""" + ) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class ElementIdentifiers(Data): + """ + A list of unique identifiers for values within a dataset, e.g. rows of a DynamicTable. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field( + "element_id", json_schema_extra={"linkml_meta": {"ifabsent": "string(element_id)"}} + ) + + +class DynamicTableRegion(VectorData): + """ + DynamicTableRegion provides a link from one table to an index or region of another. The `table` attribute is a link to another `DynamicTable`, indicating which table is referenced, and the data is int(s) indicating the row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to associate rows with repeated meta-data without data duplication. They can also be used to create hierarchical relationships between multiple `DynamicTable`s. `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create ragged references, so a single cell of a `DynamicTable` can reference many rows of another `DynamicTable`. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + table: Optional[DynamicTable] = Field( + None, description="""Reference to the DynamicTable object that this region applies to.""" + ) + description: Optional[str] = Field( + None, description="""Description of what this table region points to.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class VocabData(VectorData): + """ + Data that come from a controlled vocabulary of text values. A data value of i corresponds to the i-th element in the 'vocabulary' array attribute. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + vocabulary: Optional[str] = Field( + None, description="""The available items in the controlled vocabulary.""" + ) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class DynamicTable(DynamicTableMixin): + """ + A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). These datasets represent different columns in the table. Apart from a column that contains unique identifiers for each row, there are no other required datasets. Users are free to add any number of custom VectorData objects (columns) here. DynamicTable also supports ragged array columns, where each element can be of a different size. To add a ragged array column, use a VectorIndex type to index the corresponding VectorData type. See documentation for VectorData and VectorIndex for more details. Unlike a compound data type, which is analogous to storing an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + colnames: Optional[str] = Field( + None, + description="""The names of the columns in this table. This should be used to specify an order to the columns.""", + ) + description: Optional[str] = Field( + None, description="""Description of what is in this dynamic table.""" + ) + id: NDArray[Shape["* num_rows"], int] = Field( + ..., + description="""Array of unique identifiers for the rows of this dynamic table.""", + json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, + ) + vector_data: Optional[List[VectorData]] = Field( + None, description="""Vector columns, including index columns, of this dynamic table.""" + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +VectorData.model_rebuild() +VectorIndex.model_rebuild() +ElementIdentifiers.model_rebuild() +DynamicTableRegion.model_rebuild() +VocabData.model_rebuild() +DynamicTable.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/namespace.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/namespace.py new file mode 100644 index 0000000..62d22cb --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_0/namespace.py @@ -0,0 +1,83 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_2_0.hdmf_common_sparse import ( + CSRMatrix, + CSRMatrixIndices, + CSRMatrixIndptr, + CSRMatrixData, +) +from ...hdmf_common.v1_2_0.hdmf_common_table import ( + VectorData, + VectorIndex, + ElementIdentifiers, + DynamicTableRegion, + VocabData, + DynamicTable, +) +from ...hdmf_common.v1_2_0.hdmf_common_base import Data, Container + +metamodel_version = "None" +version = "1.2.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": True}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common/", + "description": "Common data structures provided by HDMF", + "id": "hdmf-common", + "imports": [ + "hdmf-common.base", + "hdmf-common.table", + "hdmf-common.sparse", + "hdmf-common.nwb.language", + ], + "name": "hdmf-common", + } +) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/__init__.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/__init__.py @@ -0,0 +1 @@ + diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/hdmf_common_base.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/hdmf_common_base.py new file mode 100644 index 0000000..c891ed8 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/hdmf_common_base.py @@ -0,0 +1,104 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np + +metamodel_version = "None" +version = "1.2.1" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.base/", + "id": "hdmf-common.base", + "imports": ["hdmf-common.nwb.language"], + "name": "hdmf-common.base", + } +) + + +class Data(ConfiguredBaseModel): + """ + An abstract data type for a dataset. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + name: str = Field(...) + + +class Container(ConfiguredBaseModel): + """ + An abstract data type for a group storing collections of data and metadata. Base type for all data and metadata containers. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + name: str = Field(...) + + +class SimpleMultiContainer(Container): + """ + A simple Container for holding onto multiple containers + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + children: Optional[List[Container]] = Field( + None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "Container"}]}} + ) + name: str = Field(...) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +Data.model_rebuild() +Container.model_rebuild() +SimpleMultiContainer.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/hdmf_common_sparse.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/hdmf_common_sparse.py new file mode 100644 index 0000000..9e2e7ce --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/hdmf_common_sparse.py @@ -0,0 +1,126 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_2_1.hdmf_common_base import Container + +metamodel_version = "None" +version = "1.2.1" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.sparse/", + "id": "hdmf-common.sparse", + "imports": ["hdmf-common.base", "hdmf-common.nwb.language"], + "name": "hdmf-common.sparse", + } +) + + +class CSRMatrix(Container): + """ + a compressed sparse row matrix + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.sparse", "tree_root": True} + ) + + name: str = Field(...) + shape: Optional[int] = Field(None, description="""the shape of this sparse matrix""") + indices: CSRMatrixIndices = Field(..., description="""column indices""") + indptr: CSRMatrixIndptr = Field(..., description="""index pointer""") + data: CSRMatrixData = Field(..., description="""values in the matrix""") + + +class CSRMatrixIndices(ConfiguredBaseModel): + """ + column indices + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) + + name: Literal["indices"] = Field( + "indices", + json_schema_extra={ + "linkml_meta": {"equals_string": "indices", "ifabsent": "string(indices)"} + }, + ) + + +class CSRMatrixIndptr(ConfiguredBaseModel): + """ + index pointer + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) + + name: Literal["indptr"] = Field( + "indptr", + json_schema_extra={ + "linkml_meta": {"equals_string": "indptr", "ifabsent": "string(indptr)"} + }, + ) + + +class CSRMatrixData(ConfiguredBaseModel): + """ + values in the matrix + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) + + name: Literal["data"] = Field( + "data", + json_schema_extra={"linkml_meta": {"equals_string": "data", "ifabsent": "string(data)"}}, + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +CSRMatrix.model_rebuild() +CSRMatrixIndices.model_rebuild() +CSRMatrixIndptr.model_rebuild() +CSRMatrixData.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/hdmf_common_table.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/hdmf_common_table.py new file mode 100644 index 0000000..fd4377f --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/hdmf_common_table.py @@ -0,0 +1,449 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +import numpy as np +from ...hdmf_common.v1_2_1.hdmf_common_base import Data, Container +from pandas import DataFrame +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union, overload, Tuple +from numpydantic import NDArray, Shape +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator, model_validator + +metamodel_version = "None" +version = "1.2.1" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +NUMPYDANTIC_VERSION = "1.2.1" + + +class VectorDataMixin(BaseModel): + """ + Mixin class to give VectorData indexing abilities + """ + + _index: Optional["VectorIndex"] = None + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + + def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + return self._index[item] + else: + return self.array[item] + + def __setitem__(self, key, value) -> None: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class VectorIndexMixin(BaseModel): + """ + Mixin class to give VectorIndex indexing abilities + """ + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + target: Optional["VectorData"] = None + + def _getitem_helper(self, arg: int): + """ + Mimicking :func:`hdmf.common.table.VectorIndex.__getitem_helper` + """ + + start = 0 if arg == 0 else self.array[arg - 1] + end = self.array[arg] + return self.target.array[slice(start, end)] + + def __getitem__(self, item: Union[int, slice]) -> Any: + if self.target is None: + return self.array[item] + elif type(self.target).__name__ == "VectorData": + if isinstance(item, int): + return self._getitem_helper(item) + else: + idx = range(*item.indices(len(self.array))) + return [self._getitem_helper(i) for i in idx] + else: + raise NotImplementedError("DynamicTableRange not supported yet") + + def __setitem__(self, key, value) -> None: + if self._index: + # VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class DynamicTableMixin(BaseModel): + """ + Mixin to make DynamicTable subclasses behave like tables/dataframes + + Mimicking some of the behavior from :class:`hdmf.common.table.DynamicTable` + but simplifying along the way :) + """ + + model_config = ConfigDict(extra="allow") + __pydantic_extra__: Dict[str, Union[list, "NDArray", "VectorData"]] + NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( + "name", + "colnames", + "description", + ) + + # overridden by subclass but implemented here for testing and typechecking purposes :) + colnames: List[str] = Field(default_factory=list) + + @property + def _columns(self) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + return {k: getattr(self, k) for i, k in enumerate(self.colnames)} + + @property + def _columns_list(self) -> List[Union[list, "NDArray", "VectorData"]]: + return [getattr(self, k) for i, k in enumerate(self.colnames)] + + @overload + def __getitem__(self, item: str) -> Union[list, "NDArray", "VectorData"]: ... + + @overload + def __getitem__(self, item: int) -> DataFrame: ... + + @overload + def __getitem__(self, item: Tuple[int, Union[int, str]]) -> Any: ... + + @overload + def __getitem__(self, item: Tuple[Union[int, slice], ...]) -> Union[ + DataFrame, + list, + "NDArray", + "VectorData", + ]: ... + + @overload + def __getitem__(self, item: slice) -> DataFrame: ... + + def __getitem__( + self, + item: Union[ + str, + int, + slice, + Tuple[int, Union[int, str]], + Tuple[Union[int, slice], ...], + ], + ) -> Any: + """ + Get an item from the table + + If item is... + + - ``str`` : get the column with this name + - ``int`` : get the row at this index + - ``tuple[int, int]`` : get a specific cell value eg. (0,1) gets the 0th row and 1st column + - ``tuple[int, str]`` : get a specific cell value eg. (0, 'colname') + gets the 0th row from ``colname`` + - ``tuple[int | slice, int | slice]`` : get a range of cells from a range of columns. + returns as a :class:`pandas.DataFrame` + """ + if isinstance(item, str): + return self._columns[item] + if isinstance(item, (int, slice)): + return DataFrame.from_dict(self._slice_range(item)) + elif isinstance(item, tuple): + if len(item) != 2: + raise ValueError( + "DynamicTables are 2-dimensional, can't index with more than 2 indices like" + f" {item}" + ) + + # all other cases are tuples of (rows, cols) + rows, cols = item + if isinstance(cols, (int, slice)): + cols = self.colnames[cols] + data = self._slice_range(rows, cols) + return DataFrame.from_dict(data) + else: + raise ValueError(f"Unsure how to get item with key {item}") + + def _slice_range( + self, rows: Union[int, slice], cols: Optional[Union[str, List[str]]] = None + ) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + if cols is None: + cols = self.colnames + elif isinstance(cols, str): + cols = [cols] + + data = {k: self._columns[k][rows] for k in cols} + return data + + def __setitem__(self, key: str, value: Any) -> None: + raise NotImplementedError("TODO") + + def __setattr__(self, key: str, value: Union[list, "NDArray", "VectorData"]): + """ + Add a column, appending it to ``colnames`` + """ + # don't use this while building the model + if not getattr(self, "__pydantic_complete__", False): + return super().__setattr__(key, value) + + if key not in self.model_fields_set and not key.endswith("_index"): + self.colnames.append(key) + + return super().__setattr__(key, value) + + @model_validator(mode="before") + @classmethod + def create_colnames(cls, model: Dict[str, Any]): + """ + Construct colnames from arguments. + + the model dict is ordered after python3.6, so we can use that minus + anything in :attr:`.NON_COLUMN_FIELDS` to determine order implied from passage order + """ + if "colnames" not in model: + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") + ] + model["colnames"] = colnames + else: + # add any columns not explicitly given an order at the end + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS + and not k.endswith("_index") + and k not in model["colnames"].keys() + ] + model["colnames"].extend(colnames) + return model + + @model_validator(mode="after") + def resolve_targets(self) -> "DynamicTableMixin": + """ + Ensure that any implicitly indexed columns are linked, and create backlinks + """ + for key, col in self._columns.items(): + if isinstance(col, VectorData): + # find an index + idx = None + for field_name in self.model_fields_set: + # implicit name-based index + field = getattr(self, field_name) + if isinstance(field, VectorIndex): + if field_name == f"{key}_index": + idx = field + break + elif field.target is col: + idx = field + break + if idx is not None: + col._index = idx + idx.target = col + return self + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.table/", + "id": "hdmf-common.table", + "imports": ["hdmf-common.base", "hdmf-common.nwb.language"], + "name": "hdmf-common.table", + } +) + + +class VectorData(VectorDataMixin): + """ + An n-dimensional dataset representing a column of a DynamicTable. If used without an accompanying VectorIndex, first dimension is along the rows of the DynamicTable and each step along the first dimension is a cell of the larger table. VectorData can also be used to represent a ragged array if paired with a VectorIndex. This allows for storing arrays of varying length in a single cell of the DynamicTable by indexing into this VectorData. The first vector is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], and so on. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class VectorIndex(VectorIndexMixin): + """ + Used with VectorData to encode a ragged array. An array of indices into the first dimension of the target VectorData, and forming a map between the rows of a DynamicTable and the indices of the VectorData. The name of the VectorIndex is expected to be the name of the target VectorData object followed by \"_index\". + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + target: Optional[VectorData] = Field( + None, description="""Reference to the target dataset that this index applies to.""" + ) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class ElementIdentifiers(Data): + """ + A list of unique identifiers for values within a dataset, e.g. rows of a DynamicTable. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field( + "element_id", json_schema_extra={"linkml_meta": {"ifabsent": "string(element_id)"}} + ) + + +class DynamicTableRegion(VectorData): + """ + DynamicTableRegion provides a link from one table to an index or region of another. The `table` attribute is a link to another `DynamicTable`, indicating which table is referenced, and the data is int(s) indicating the row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to associate rows with repeated meta-data without data duplication. They can also be used to create hierarchical relationships between multiple `DynamicTable`s. `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create ragged references, so a single cell of a `DynamicTable` can reference many rows of another `DynamicTable`. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + table: Optional[DynamicTable] = Field( + None, description="""Reference to the DynamicTable object that this region applies to.""" + ) + description: Optional[str] = Field( + None, description="""Description of what this table region points to.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class VocabData(VectorData): + """ + Data that come from a controlled vocabulary of text values. A data value of i corresponds to the i-th element in the 'vocabulary' array attribute. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + vocabulary: Optional[str] = Field( + None, description="""The available items in the controlled vocabulary.""" + ) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class DynamicTable(DynamicTableMixin): + """ + A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). These datasets represent different columns in the table. Apart from a column that contains unique identifiers for each row, there are no other required datasets. Users are free to add any number of custom VectorData objects (columns) here. DynamicTable also supports ragged array columns, where each element can be of a different size. To add a ragged array column, use a VectorIndex type to index the corresponding VectorData type. See documentation for VectorData and VectorIndex for more details. Unlike a compound data type, which is analogous to storing an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + colnames: Optional[str] = Field( + None, + description="""The names of the columns in this table. This should be used to specify an order to the columns.""", + ) + description: Optional[str] = Field( + None, description="""Description of what is in this dynamic table.""" + ) + id: NDArray[Shape["* num_rows"], int] = Field( + ..., + description="""Array of unique identifiers for the rows of this dynamic table.""", + json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, + ) + vector_data: Optional[List[VectorData]] = Field( + None, description="""Vector columns, including index columns, of this dynamic table.""" + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +VectorData.model_rebuild() +VectorIndex.model_rebuild() +ElementIdentifiers.model_rebuild() +DynamicTableRegion.model_rebuild() +VocabData.model_rebuild() +DynamicTable.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/namespace.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/namespace.py new file mode 100644 index 0000000..55f5dc6 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_2_1/namespace.py @@ -0,0 +1,83 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_2_1.hdmf_common_sparse import ( + CSRMatrix, + CSRMatrixIndices, + CSRMatrixIndptr, + CSRMatrixData, +) +from ...hdmf_common.v1_2_1.hdmf_common_base import Data, Container, SimpleMultiContainer +from ...hdmf_common.v1_2_1.hdmf_common_table import ( + VectorData, + VectorIndex, + ElementIdentifiers, + DynamicTableRegion, + VocabData, + DynamicTable, +) + +metamodel_version = "None" +version = "1.2.1" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": True}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common/", + "description": "Common data structures provided by HDMF", + "id": "hdmf-common", + "imports": [ + "hdmf-common.base", + "hdmf-common.table", + "hdmf-common.sparse", + "hdmf-common.nwb.language", + ], + "name": "hdmf-common", + } +) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/__init__.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/__init__.py @@ -0,0 +1 @@ + diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_base.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_base.py new file mode 100644 index 0000000..63bbcf2 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_base.py @@ -0,0 +1,104 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np + +metamodel_version = "None" +version = "1.3.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.base/", + "id": "hdmf-common.base", + "imports": ["hdmf-common.nwb.language"], + "name": "hdmf-common.base", + } +) + + +class Data(ConfiguredBaseModel): + """ + An abstract data type for a dataset. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + name: str = Field(...) + + +class Container(ConfiguredBaseModel): + """ + An abstract data type for a group storing collections of data and metadata. Base type for all data and metadata containers. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + name: str = Field(...) + + +class SimpleMultiContainer(Container): + """ + A simple Container for holding onto multiple containers. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + children: Optional[List[Container]] = Field( + None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "Container"}]}} + ) + name: str = Field(...) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +Data.model_rebuild() +Container.model_rebuild() +SimpleMultiContainer.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_resources.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_resources.py new file mode 100644 index 0000000..81f3031 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_resources.py @@ -0,0 +1,181 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_3_0.hdmf_common_base import Container, Data + +metamodel_version = "None" +version = "1.3.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.resources/", + "id": "hdmf-common.resources", + "imports": ["hdmf-common.base", "hdmf-common.nwb.language"], + "name": "hdmf-common.resources", + } +) + + +class ExternalResources(Container): + """ + A set of four tables for tracking external resource references in a file. NOTE: this data type is in beta testing and is subject to change in a later version. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.resources", "tree_root": True} + ) + + name: str = Field(...) + keys: ExternalResourcesKeys = Field( + ..., + description="""A table for storing user terms that are used to refer to external resources.""", + ) + resources: ExternalResourcesResources = Field( + ..., description="""A table for mapping user terms (i.e., keys) to resource entities.""" + ) + objects: ExternalResourcesObjects = Field( + ..., + description="""A table for identifying which objects in a file contain references to external resources.""", + ) + object_keys: ExternalResourcesObjectKeys = Field( + ..., description="""A table for identifying which objects use which keys.""" + ) + + +class ExternalResourcesKeys(Data): + """ + A table for storing user terms that are used to refer to external resources. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.resources"}) + + name: Literal["keys"] = Field( + "keys", + json_schema_extra={"linkml_meta": {"equals_string": "keys", "ifabsent": "string(keys)"}}, + ) + key_name: str = Field( + ..., + description="""The user term that maps to one or more resources in the 'resources' table.""", + ) + + +class ExternalResourcesResources(Data): + """ + A table for mapping user terms (i.e., keys) to resource entities. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.resources"}) + + name: Literal["resources"] = Field( + "resources", + json_schema_extra={ + "linkml_meta": {"equals_string": "resources", "ifabsent": "string(resources)"} + }, + ) + keytable_idx: np.uint64 = Field( + ..., description="""The index to the key in the 'keys' table.""" + ) + resource_name: str = Field( + ..., + description="""The name of the online resource (e.g., website, database) that has the entity.""", + ) + resource_id: str = Field( + ..., description="""The unique identifier for the resource entity at the resource.""" + ) + uri: str = Field( + ..., + description="""The URI for the resource entity this reference applies to. This can be an empty string.""", + ) + + +class ExternalResourcesObjects(Data): + """ + A table for identifying which objects in a file contain references to external resources. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.resources"}) + + name: Literal["objects"] = Field( + "objects", + json_schema_extra={ + "linkml_meta": {"equals_string": "objects", "ifabsent": "string(objects)"} + }, + ) + object_id: str = Field(..., description="""The UUID for the object.""") + field: str = Field( + ..., + description="""The field of the object. This can be an empty string if the object is a dataset and the field is the dataset values.""", + ) + + +class ExternalResourcesObjectKeys(Data): + """ + A table for identifying which objects use which keys. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.resources"}) + + name: Literal["object_keys"] = Field( + "object_keys", + json_schema_extra={ + "linkml_meta": {"equals_string": "object_keys", "ifabsent": "string(object_keys)"} + }, + ) + objecttable_idx: np.uint64 = Field( + ..., description="""The index to the 'objects' table for the object that holds the key.""" + ) + keytable_idx: np.uint64 = Field( + ..., description="""The index to the 'keys' table for the key.""" + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +ExternalResources.model_rebuild() +ExternalResourcesKeys.model_rebuild() +ExternalResourcesResources.model_rebuild() +ExternalResourcesObjects.model_rebuild() +ExternalResourcesObjectKeys.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_sparse.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_sparse.py new file mode 100644 index 0000000..fe3047c --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_sparse.py @@ -0,0 +1,110 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_3_0.hdmf_common_base import Container +from numpydantic import NDArray, Shape + +metamodel_version = "None" +version = "1.3.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +NUMPYDANTIC_VERSION = "1.2.1" +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.sparse/", + "id": "hdmf-common.sparse", + "imports": ["hdmf-common.base", "hdmf-common.nwb.language"], + "name": "hdmf-common.sparse", + } +) + + +class CSRMatrix(Container): + """ + A compressed sparse row matrix. Data are stored in the standard CSR format, where column indices for row i are stored in indices[indptr[i]:indptr[i+1]] and their corresponding values are stored in data[indptr[i]:indptr[i+1]]. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.sparse", "tree_root": True} + ) + + name: str = Field(...) + shape: Optional[np.uint64] = Field( + None, description="""The shape (number of rows, number of columns) of this sparse matrix.""" + ) + indices: NDArray[Shape["* number_of_non_zero_values"], np.uint64] = Field( + ..., + description="""The column indices.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_non_zero_values"}]}} + }, + ) + indptr: NDArray[Shape["* number_of_rows_in_the_matrix_1"], np.uint64] = Field( + ..., + description="""The row index pointer.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_rows_in_the_matrix_1"}]}} + }, + ) + data: CSRMatrixData = Field(..., description="""The non-zero values in the matrix.""") + + +class CSRMatrixData(ConfiguredBaseModel): + """ + The non-zero values in the matrix. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) + + name: Literal["data"] = Field( + "data", + json_schema_extra={"linkml_meta": {"equals_string": "data", "ifabsent": "string(data)"}}, + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +CSRMatrix.model_rebuild() +CSRMatrixData.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_table.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_table.py new file mode 100644 index 0000000..7f3f848 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/hdmf_common_table.py @@ -0,0 +1,449 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +import numpy as np +from ...hdmf_common.v1_3_0.hdmf_common_base import Data, Container +from pandas import DataFrame +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union, overload, Tuple +from numpydantic import NDArray, Shape +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator, model_validator + +metamodel_version = "None" +version = "1.3.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +NUMPYDANTIC_VERSION = "1.2.1" + + +class VectorDataMixin(BaseModel): + """ + Mixin class to give VectorData indexing abilities + """ + + _index: Optional["VectorIndex"] = None + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + + def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + return self._index[item] + else: + return self.array[item] + + def __setitem__(self, key, value) -> None: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class VectorIndexMixin(BaseModel): + """ + Mixin class to give VectorIndex indexing abilities + """ + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + target: Optional["VectorData"] = None + + def _getitem_helper(self, arg: int): + """ + Mimicking :func:`hdmf.common.table.VectorIndex.__getitem_helper` + """ + + start = 0 if arg == 0 else self.array[arg - 1] + end = self.array[arg] + return self.target.array[slice(start, end)] + + def __getitem__(self, item: Union[int, slice]) -> Any: + if self.target is None: + return self.array[item] + elif type(self.target).__name__ == "VectorData": + if isinstance(item, int): + return self._getitem_helper(item) + else: + idx = range(*item.indices(len(self.array))) + return [self._getitem_helper(i) for i in idx] + else: + raise NotImplementedError("DynamicTableRange not supported yet") + + def __setitem__(self, key, value) -> None: + if self._index: + # VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class DynamicTableMixin(BaseModel): + """ + Mixin to make DynamicTable subclasses behave like tables/dataframes + + Mimicking some of the behavior from :class:`hdmf.common.table.DynamicTable` + but simplifying along the way :) + """ + + model_config = ConfigDict(extra="allow") + __pydantic_extra__: Dict[str, Union[list, "NDArray", "VectorData"]] + NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( + "name", + "colnames", + "description", + ) + + # overridden by subclass but implemented here for testing and typechecking purposes :) + colnames: List[str] = Field(default_factory=list) + + @property + def _columns(self) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + return {k: getattr(self, k) for i, k in enumerate(self.colnames)} + + @property + def _columns_list(self) -> List[Union[list, "NDArray", "VectorData"]]: + return [getattr(self, k) for i, k in enumerate(self.colnames)] + + @overload + def __getitem__(self, item: str) -> Union[list, "NDArray", "VectorData"]: ... + + @overload + def __getitem__(self, item: int) -> DataFrame: ... + + @overload + def __getitem__(self, item: Tuple[int, Union[int, str]]) -> Any: ... + + @overload + def __getitem__(self, item: Tuple[Union[int, slice], ...]) -> Union[ + DataFrame, + list, + "NDArray", + "VectorData", + ]: ... + + @overload + def __getitem__(self, item: slice) -> DataFrame: ... + + def __getitem__( + self, + item: Union[ + str, + int, + slice, + Tuple[int, Union[int, str]], + Tuple[Union[int, slice], ...], + ], + ) -> Any: + """ + Get an item from the table + + If item is... + + - ``str`` : get the column with this name + - ``int`` : get the row at this index + - ``tuple[int, int]`` : get a specific cell value eg. (0,1) gets the 0th row and 1st column + - ``tuple[int, str]`` : get a specific cell value eg. (0, 'colname') + gets the 0th row from ``colname`` + - ``tuple[int | slice, int | slice]`` : get a range of cells from a range of columns. + returns as a :class:`pandas.DataFrame` + """ + if isinstance(item, str): + return self._columns[item] + if isinstance(item, (int, slice)): + return DataFrame.from_dict(self._slice_range(item)) + elif isinstance(item, tuple): + if len(item) != 2: + raise ValueError( + "DynamicTables are 2-dimensional, can't index with more than 2 indices like" + f" {item}" + ) + + # all other cases are tuples of (rows, cols) + rows, cols = item + if isinstance(cols, (int, slice)): + cols = self.colnames[cols] + data = self._slice_range(rows, cols) + return DataFrame.from_dict(data) + else: + raise ValueError(f"Unsure how to get item with key {item}") + + def _slice_range( + self, rows: Union[int, slice], cols: Optional[Union[str, List[str]]] = None + ) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + if cols is None: + cols = self.colnames + elif isinstance(cols, str): + cols = [cols] + + data = {k: self._columns[k][rows] for k in cols} + return data + + def __setitem__(self, key: str, value: Any) -> None: + raise NotImplementedError("TODO") + + def __setattr__(self, key: str, value: Union[list, "NDArray", "VectorData"]): + """ + Add a column, appending it to ``colnames`` + """ + # don't use this while building the model + if not getattr(self, "__pydantic_complete__", False): + return super().__setattr__(key, value) + + if key not in self.model_fields_set and not key.endswith("_index"): + self.colnames.append(key) + + return super().__setattr__(key, value) + + @model_validator(mode="before") + @classmethod + def create_colnames(cls, model: Dict[str, Any]): + """ + Construct colnames from arguments. + + the model dict is ordered after python3.6, so we can use that minus + anything in :attr:`.NON_COLUMN_FIELDS` to determine order implied from passage order + """ + if "colnames" not in model: + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") + ] + model["colnames"] = colnames + else: + # add any columns not explicitly given an order at the end + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS + and not k.endswith("_index") + and k not in model["colnames"].keys() + ] + model["colnames"].extend(colnames) + return model + + @model_validator(mode="after") + def resolve_targets(self) -> "DynamicTableMixin": + """ + Ensure that any implicitly indexed columns are linked, and create backlinks + """ + for key, col in self._columns.items(): + if isinstance(col, VectorData): + # find an index + idx = None + for field_name in self.model_fields_set: + # implicit name-based index + field = getattr(self, field_name) + if isinstance(field, VectorIndex): + if field_name == f"{key}_index": + idx = field + break + elif field.target is col: + idx = field + break + if idx is not None: + col._index = idx + idx.target = col + return self + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.table/", + "id": "hdmf-common.table", + "imports": ["hdmf-common.base", "hdmf-common.nwb.language"], + "name": "hdmf-common.table", + } +) + + +class VectorData(VectorDataMixin): + """ + An n-dimensional dataset representing a column of a DynamicTable. If used without an accompanying VectorIndex, first dimension is along the rows of the DynamicTable and each step along the first dimension is a cell of the larger table. VectorData can also be used to represent a ragged array if paired with a VectorIndex. This allows for storing arrays of varying length in a single cell of the DynamicTable by indexing into this VectorData. The first vector is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], and so on. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class VectorIndex(VectorIndexMixin): + """ + Used with VectorData to encode a ragged array. An array of indices into the first dimension of the target VectorData, and forming a map between the rows of a DynamicTable and the indices of the VectorData. The name of the VectorIndex is expected to be the name of the target VectorData object followed by \"_index\". + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + target: Optional[VectorData] = Field( + None, description="""Reference to the target dataset that this index applies to.""" + ) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class ElementIdentifiers(Data): + """ + A list of unique identifiers for values within a dataset, e.g. rows of a DynamicTable. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field( + "element_id", json_schema_extra={"linkml_meta": {"ifabsent": "string(element_id)"}} + ) + + +class DynamicTableRegion(VectorData): + """ + DynamicTableRegion provides a link from one table to an index or region of another. The `table` attribute is a link to another `DynamicTable`, indicating which table is referenced, and the data is int(s) indicating the row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to associate rows with repeated meta-data without data duplication. They can also be used to create hierarchical relationships between multiple `DynamicTable`s. `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create ragged references, so a single cell of a `DynamicTable` can reference many rows of another `DynamicTable`. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + table: Optional[DynamicTable] = Field( + None, description="""Reference to the DynamicTable object that this region applies to.""" + ) + description: Optional[str] = Field( + None, description="""Description of what this table region points to.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class VocabData(VectorData): + """ + Data that come from a controlled vocabulary of text values. A data value of i corresponds to the i-th element in the 'vocabulary' array attribute. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + vocabulary: Optional[str] = Field( + None, description="""The available items in the controlled vocabulary.""" + ) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class DynamicTable(DynamicTableMixin): + """ + A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). These datasets represent different columns in the table. Apart from a column that contains unique identifiers for each row, there are no other required datasets. Users are free to add any number of custom VectorData objects (columns) here. DynamicTable also supports ragged array columns, where each element can be of a different size. To add a ragged array column, use a VectorIndex type to index the corresponding VectorData type. See documentation for VectorData and VectorIndex for more details. Unlike a compound data type, which is analogous to storing an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + colnames: Optional[str] = Field( + None, + description="""The names of the columns in this table. This should be used to specify an order to the columns.""", + ) + description: Optional[str] = Field( + None, description="""Description of what is in this dynamic table.""" + ) + id: NDArray[Shape["* num_rows"], int] = Field( + ..., + description="""Array of unique identifiers for the rows of this dynamic table.""", + json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, + ) + vector_data: Optional[List[VectorData]] = Field( + None, description="""Vector columns, including index columns, of this dynamic table.""" + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +VectorData.model_rebuild() +VectorIndex.model_rebuild() +ElementIdentifiers.model_rebuild() +DynamicTableRegion.model_rebuild() +VocabData.model_rebuild() +DynamicTable.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/namespace.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/namespace.py new file mode 100644 index 0000000..a2dcc70 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_3_0/namespace.py @@ -0,0 +1,86 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_3_0.hdmf_common_resources import ( + ExternalResources, + ExternalResourcesKeys, + ExternalResourcesResources, + ExternalResourcesObjects, + ExternalResourcesObjectKeys, +) +from ...hdmf_common.v1_3_0.hdmf_common_base import Data, Container, SimpleMultiContainer +from ...hdmf_common.v1_3_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_3_0.hdmf_common_table import ( + VectorData, + VectorIndex, + ElementIdentifiers, + DynamicTableRegion, + VocabData, + DynamicTable, +) + +metamodel_version = "None" +version = "1.3.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": True}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common/", + "description": "Common data structures provided by HDMF", + "id": "hdmf-common", + "imports": [ + "hdmf-common.base", + "hdmf-common.table", + "hdmf-common.sparse", + "hdmf-common.resources", + "hdmf-common.nwb.language", + ], + "name": "hdmf-common", + } +) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/__init__.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/__init__.py @@ -0,0 +1 @@ + diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/hdmf_common_base.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/hdmf_common_base.py new file mode 100644 index 0000000..c26f4f8 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/hdmf_common_base.py @@ -0,0 +1,104 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np + +metamodel_version = "None" +version = "1.4.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.base/", + "id": "hdmf-common.base", + "imports": ["hdmf-common.nwb.language"], + "name": "hdmf-common.base", + } +) + + +class Data(ConfiguredBaseModel): + """ + An abstract data type for a dataset. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + name: str = Field(...) + + +class Container(ConfiguredBaseModel): + """ + An abstract data type for a group storing collections of data and metadata. Base type for all data and metadata containers. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + name: str = Field(...) + + +class SimpleMultiContainer(Container): + """ + A simple Container for holding onto multiple containers. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + children: Optional[List[Container]] = Field( + None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "Container"}]}} + ) + name: str = Field(...) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +Data.model_rebuild() +Container.model_rebuild() +SimpleMultiContainer.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/hdmf_common_sparse.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/hdmf_common_sparse.py new file mode 100644 index 0000000..83e31dd --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/hdmf_common_sparse.py @@ -0,0 +1,110 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_4_0.hdmf_common_base import Container +from numpydantic import NDArray, Shape + +metamodel_version = "None" +version = "1.4.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +NUMPYDANTIC_VERSION = "1.2.1" +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.sparse/", + "id": "hdmf-common.sparse", + "imports": ["hdmf-common.base", "hdmf-common.nwb.language"], + "name": "hdmf-common.sparse", + } +) + + +class CSRMatrix(Container): + """ + A compressed sparse row matrix. Data are stored in the standard CSR format, where column indices for row i are stored in indices[indptr[i]:indptr[i+1]] and their corresponding values are stored in data[indptr[i]:indptr[i+1]]. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.sparse", "tree_root": True} + ) + + name: str = Field(...) + shape: Optional[np.uint64] = Field( + None, description="""The shape (number of rows, number of columns) of this sparse matrix.""" + ) + indices: NDArray[Shape["* number_of_non_zero_values"], np.uint64] = Field( + ..., + description="""The column indices.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_non_zero_values"}]}} + }, + ) + indptr: NDArray[Shape["* number_of_rows_in_the_matrix_1"], np.uint64] = Field( + ..., + description="""The row index pointer.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_rows_in_the_matrix_1"}]}} + }, + ) + data: CSRMatrixData = Field(..., description="""The non-zero values in the matrix.""") + + +class CSRMatrixData(ConfiguredBaseModel): + """ + The non-zero values in the matrix. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) + + name: Literal["data"] = Field( + "data", + json_schema_extra={"linkml_meta": {"equals_string": "data", "ifabsent": "string(data)"}}, + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +CSRMatrix.model_rebuild() +CSRMatrixData.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/hdmf_common_table.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/hdmf_common_table.py new file mode 100644 index 0000000..20c9a63 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/hdmf_common_table.py @@ -0,0 +1,422 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +import numpy as np +from ...hdmf_common.v1_4_0.hdmf_common_base import Data, Container +from pandas import DataFrame +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union, overload, Tuple +from numpydantic import NDArray, Shape +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator, model_validator + +metamodel_version = "None" +version = "1.4.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +NUMPYDANTIC_VERSION = "1.2.1" + + +class VectorDataMixin(BaseModel): + """ + Mixin class to give VectorData indexing abilities + """ + + _index: Optional["VectorIndex"] = None + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + + def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + return self._index[item] + else: + return self.array[item] + + def __setitem__(self, key, value) -> None: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class VectorIndexMixin(BaseModel): + """ + Mixin class to give VectorIndex indexing abilities + """ + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + target: Optional["VectorData"] = None + + def _getitem_helper(self, arg: int): + """ + Mimicking :func:`hdmf.common.table.VectorIndex.__getitem_helper` + """ + + start = 0 if arg == 0 else self.array[arg - 1] + end = self.array[arg] + return self.target.array[slice(start, end)] + + def __getitem__(self, item: Union[int, slice]) -> Any: + if self.target is None: + return self.array[item] + elif type(self.target).__name__ == "VectorData": + if isinstance(item, int): + return self._getitem_helper(item) + else: + idx = range(*item.indices(len(self.array))) + return [self._getitem_helper(i) for i in idx] + else: + raise NotImplementedError("DynamicTableRange not supported yet") + + def __setitem__(self, key, value) -> None: + if self._index: + # VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class DynamicTableMixin(BaseModel): + """ + Mixin to make DynamicTable subclasses behave like tables/dataframes + + Mimicking some of the behavior from :class:`hdmf.common.table.DynamicTable` + but simplifying along the way :) + """ + + model_config = ConfigDict(extra="allow") + __pydantic_extra__: Dict[str, Union[list, "NDArray", "VectorData"]] + NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( + "name", + "colnames", + "description", + ) + + # overridden by subclass but implemented here for testing and typechecking purposes :) + colnames: List[str] = Field(default_factory=list) + + @property + def _columns(self) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + return {k: getattr(self, k) for i, k in enumerate(self.colnames)} + + @property + def _columns_list(self) -> List[Union[list, "NDArray", "VectorData"]]: + return [getattr(self, k) for i, k in enumerate(self.colnames)] + + @overload + def __getitem__(self, item: str) -> Union[list, "NDArray", "VectorData"]: ... + + @overload + def __getitem__(self, item: int) -> DataFrame: ... + + @overload + def __getitem__(self, item: Tuple[int, Union[int, str]]) -> Any: ... + + @overload + def __getitem__(self, item: Tuple[Union[int, slice], ...]) -> Union[ + DataFrame, + list, + "NDArray", + "VectorData", + ]: ... + + @overload + def __getitem__(self, item: slice) -> DataFrame: ... + + def __getitem__( + self, + item: Union[ + str, + int, + slice, + Tuple[int, Union[int, str]], + Tuple[Union[int, slice], ...], + ], + ) -> Any: + """ + Get an item from the table + + If item is... + + - ``str`` : get the column with this name + - ``int`` : get the row at this index + - ``tuple[int, int]`` : get a specific cell value eg. (0,1) gets the 0th row and 1st column + - ``tuple[int, str]`` : get a specific cell value eg. (0, 'colname') + gets the 0th row from ``colname`` + - ``tuple[int | slice, int | slice]`` : get a range of cells from a range of columns. + returns as a :class:`pandas.DataFrame` + """ + if isinstance(item, str): + return self._columns[item] + if isinstance(item, (int, slice)): + return DataFrame.from_dict(self._slice_range(item)) + elif isinstance(item, tuple): + if len(item) != 2: + raise ValueError( + "DynamicTables are 2-dimensional, can't index with more than 2 indices like" + f" {item}" + ) + + # all other cases are tuples of (rows, cols) + rows, cols = item + if isinstance(cols, (int, slice)): + cols = self.colnames[cols] + data = self._slice_range(rows, cols) + return DataFrame.from_dict(data) + else: + raise ValueError(f"Unsure how to get item with key {item}") + + def _slice_range( + self, rows: Union[int, slice], cols: Optional[Union[str, List[str]]] = None + ) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + if cols is None: + cols = self.colnames + elif isinstance(cols, str): + cols = [cols] + + data = {k: self._columns[k][rows] for k in cols} + return data + + def __setitem__(self, key: str, value: Any) -> None: + raise NotImplementedError("TODO") + + def __setattr__(self, key: str, value: Union[list, "NDArray", "VectorData"]): + """ + Add a column, appending it to ``colnames`` + """ + # don't use this while building the model + if not getattr(self, "__pydantic_complete__", False): + return super().__setattr__(key, value) + + if key not in self.model_fields_set and not key.endswith("_index"): + self.colnames.append(key) + + return super().__setattr__(key, value) + + @model_validator(mode="before") + @classmethod + def create_colnames(cls, model: Dict[str, Any]): + """ + Construct colnames from arguments. + + the model dict is ordered after python3.6, so we can use that minus + anything in :attr:`.NON_COLUMN_FIELDS` to determine order implied from passage order + """ + if "colnames" not in model: + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") + ] + model["colnames"] = colnames + else: + # add any columns not explicitly given an order at the end + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS + and not k.endswith("_index") + and k not in model["colnames"].keys() + ] + model["colnames"].extend(colnames) + return model + + @model_validator(mode="after") + def resolve_targets(self) -> "DynamicTableMixin": + """ + Ensure that any implicitly indexed columns are linked, and create backlinks + """ + for key, col in self._columns.items(): + if isinstance(col, VectorData): + # find an index + idx = None + for field_name in self.model_fields_set: + # implicit name-based index + field = getattr(self, field_name) + if isinstance(field, VectorIndex): + if field_name == f"{key}_index": + idx = field + break + elif field.target is col: + idx = field + break + if idx is not None: + col._index = idx + idx.target = col + return self + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.table/", + "id": "hdmf-common.table", + "imports": ["hdmf-common.base", "hdmf-common.nwb.language"], + "name": "hdmf-common.table", + } +) + + +class VectorData(VectorDataMixin): + """ + An n-dimensional dataset representing a column of a DynamicTable. If used without an accompanying VectorIndex, first dimension is along the rows of the DynamicTable and each step along the first dimension is a cell of the larger table. VectorData can also be used to represent a ragged array if paired with a VectorIndex. This allows for storing arrays of varying length in a single cell of the DynamicTable by indexing into this VectorData. The first vector is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], and so on. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class VectorIndex(VectorIndexMixin): + """ + Used with VectorData to encode a ragged array. An array of indices into the first dimension of the target VectorData, and forming a map between the rows of a DynamicTable and the indices of the VectorData. The name of the VectorIndex is expected to be the name of the target VectorData object followed by \"_index\". + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + target: Optional[VectorData] = Field( + None, description="""Reference to the target dataset that this index applies to.""" + ) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class ElementIdentifiers(Data): + """ + A list of unique identifiers for values within a dataset, e.g. rows of a DynamicTable. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field( + "element_id", json_schema_extra={"linkml_meta": {"ifabsent": "string(element_id)"}} + ) + + +class DynamicTableRegion(VectorData): + """ + DynamicTableRegion provides a link from one table to an index or region of another. The `table` attribute is a link to another `DynamicTable`, indicating which table is referenced, and the data is int(s) indicating the row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to associate rows with repeated meta-data without data duplication. They can also be used to create hierarchical relationships between multiple `DynamicTable`s. `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create ragged references, so a single cell of a `DynamicTable` can reference many rows of another `DynamicTable`. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + table: Optional[DynamicTable] = Field( + None, description="""Reference to the DynamicTable object that this region applies to.""" + ) + description: Optional[str] = Field( + None, description="""Description of what this table region points to.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class DynamicTable(DynamicTableMixin): + """ + A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). These datasets represent different columns in the table. Apart from a column that contains unique identifiers for each row, there are no other required datasets. Users are free to add any number of custom VectorData objects (columns) here. DynamicTable also supports ragged array columns, where each element can be of a different size. To add a ragged array column, use a VectorIndex type to index the corresponding VectorData type. See documentation for VectorData and VectorIndex for more details. Unlike a compound data type, which is analogous to storing an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + colnames: Optional[str] = Field( + None, + description="""The names of the columns in this table. This should be used to specify an order to the columns.""", + ) + description: Optional[str] = Field( + None, description="""Description of what is in this dynamic table.""" + ) + id: NDArray[Shape["* num_rows"], int] = Field( + ..., + description="""Array of unique identifiers for the rows of this dynamic table.""", + json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, + ) + vector_data: Optional[List[VectorData]] = Field( + None, description="""Vector columns, including index columns, of this dynamic table.""" + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +VectorData.model_rebuild() +VectorIndex.model_rebuild() +ElementIdentifiers.model_rebuild() +DynamicTableRegion.model_rebuild() +DynamicTable.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/namespace.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/namespace.py new file mode 100644 index 0000000..db59f28 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_4_0/namespace.py @@ -0,0 +1,77 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_4_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_4_0.hdmf_common_base import Data, Container, SimpleMultiContainer +from ...hdmf_common.v1_4_0.hdmf_common_table import ( + VectorData, + VectorIndex, + ElementIdentifiers, + DynamicTableRegion, + DynamicTable, +) + +metamodel_version = "None" +version = "1.4.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": True}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common/", + "description": "Common data structures provided by HDMF", + "id": "hdmf-common", + "imports": [ + "hdmf-common.base", + "hdmf-common.table", + "hdmf-common.sparse", + "hdmf-common.nwb.language", + ], + "name": "hdmf-common", + } +) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_0/hdmf_common_table.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_0/hdmf_common_table.py index 9e2b445..ba68e69 100644 --- a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_0/hdmf_common_table.py +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_0/hdmf_common_table.py @@ -4,10 +4,11 @@ from decimal import Decimal from enum import Enum import re import sys -from typing import Any, ClassVar, List, Literal, Dict, Optional, Union -from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator import numpy as np from ...hdmf_common.v1_5_0.hdmf_common_base import Data, Container +from pandas import DataFrame +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union, overload, Tuple +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator, model_validator from numpydantic import NDArray, Shape metamodel_version = "None" @@ -47,6 +48,241 @@ class LinkMLMeta(RootModel): NUMPYDANTIC_VERSION = "1.2.1" + + +class VectorDataMixin(BaseModel): + """ + Mixin class to give VectorData indexing abilities + """ + + _index: Optional["VectorIndex"] = None + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + + def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + return self._index[item] + else: + return self.array[item] + + def __setitem__(self, key, value) -> None: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class VectorIndexMixin(BaseModel): + """ + Mixin class to give VectorIndex indexing abilities + """ + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + target: Optional["VectorData"] = None + + def _getitem_helper(self, arg: int): + """ + Mimicking :func:`hdmf.common.table.VectorIndex.__getitem_helper` + """ + + start = 0 if arg == 0 else self.array[arg - 1] + end = self.array[arg] + return self.target.array[slice(start, end)] + + def __getitem__(self, item: Union[int, slice]) -> Any: + if self.target is None: + return self.array[item] + elif type(self.target).__name__ == "VectorData": + if isinstance(item, int): + return self._getitem_helper(item) + else: + idx = range(*item.indices(len(self.array))) + return [self._getitem_helper(i) for i in idx] + else: + raise NotImplementedError("DynamicTableRange not supported yet") + + def __setitem__(self, key, value) -> None: + if self._index: + # VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class DynamicTableMixin(BaseModel): + """ + Mixin to make DynamicTable subclasses behave like tables/dataframes + + Mimicking some of the behavior from :class:`hdmf.common.table.DynamicTable` + but simplifying along the way :) + """ + + model_config = ConfigDict(extra="allow") + __pydantic_extra__: Dict[str, Union[list, "NDArray", "VectorData"]] + NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( + "name", + "colnames", + "description", + ) + + # overridden by subclass but implemented here for testing and typechecking purposes :) + colnames: List[str] = Field(default_factory=list) + + @property + def _columns(self) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + return {k: getattr(self, k) for i, k in enumerate(self.colnames)} + + @property + def _columns_list(self) -> List[Union[list, "NDArray", "VectorData"]]: + return [getattr(self, k) for i, k in enumerate(self.colnames)] + + @overload + def __getitem__(self, item: str) -> Union[list, "NDArray", "VectorData"]: ... + + @overload + def __getitem__(self, item: int) -> DataFrame: ... + + @overload + def __getitem__(self, item: Tuple[int, Union[int, str]]) -> Any: ... + + @overload + def __getitem__(self, item: Tuple[Union[int, slice], ...]) -> Union[ + DataFrame, + list, + "NDArray", + "VectorData", + ]: ... + + @overload + def __getitem__(self, item: slice) -> DataFrame: ... + + def __getitem__( + self, + item: Union[ + str, + int, + slice, + Tuple[int, Union[int, str]], + Tuple[Union[int, slice], ...], + ], + ) -> Any: + """ + Get an item from the table + + If item is... + + - ``str`` : get the column with this name + - ``int`` : get the row at this index + - ``tuple[int, int]`` : get a specific cell value eg. (0,1) gets the 0th row and 1st column + - ``tuple[int, str]`` : get a specific cell value eg. (0, 'colname') + gets the 0th row from ``colname`` + - ``tuple[int | slice, int | slice]`` : get a range of cells from a range of columns. + returns as a :class:`pandas.DataFrame` + """ + if isinstance(item, str): + return self._columns[item] + if isinstance(item, (int, slice)): + return DataFrame.from_dict(self._slice_range(item)) + elif isinstance(item, tuple): + if len(item) != 2: + raise ValueError( + "DynamicTables are 2-dimensional, can't index with more than 2 indices like" + f" {item}" + ) + + # all other cases are tuples of (rows, cols) + rows, cols = item + if isinstance(cols, (int, slice)): + cols = self.colnames[cols] + data = self._slice_range(rows, cols) + return DataFrame.from_dict(data) + else: + raise ValueError(f"Unsure how to get item with key {item}") + + def _slice_range( + self, rows: Union[int, slice], cols: Optional[Union[str, List[str]]] = None + ) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + if cols is None: + cols = self.colnames + elif isinstance(cols, str): + cols = [cols] + + data = {k: self._columns[k][rows] for k in cols} + return data + + def __setitem__(self, key: str, value: Any) -> None: + raise NotImplementedError("TODO") + + def __setattr__(self, key: str, value: Union[list, "NDArray", "VectorData"]): + """ + Add a column, appending it to ``colnames`` + """ + # don't use this while building the model + if not getattr(self, "__pydantic_complete__", False): + return super().__setattr__(key, value) + + if key not in self.model_fields_set and not key.endswith("_index"): + self.colnames.append(key) + + return super().__setattr__(key, value) + + @model_validator(mode="before") + @classmethod + def create_colnames(cls, model: Dict[str, Any]): + """ + Construct colnames from arguments. + + the model dict is ordered after python3.6, so we can use that minus + anything in :attr:`.NON_COLUMN_FIELDS` to determine order implied from passage order + """ + if "colnames" not in model: + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") + ] + model["colnames"] = colnames + else: + # add any columns not explicitly given an order at the end + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS + and not k.endswith("_index") + and k not in model["colnames"].keys() + ] + model["colnames"].extend(colnames) + return model + + @model_validator(mode="after") + def resolve_targets(self) -> "DynamicTableMixin": + """ + Ensure that any implicitly indexed columns are linked, and create backlinks + """ + for key, col in self._columns.items(): + if isinstance(col, VectorData): + # find an index + idx = None + for field_name in self.model_fields_set: + # implicit name-based index + field = getattr(self, field_name) + if isinstance(field, VectorIndex): + if field_name == f"{key}_index": + idx = field + break + elif field.target is col: + idx = field + break + if idx is not None: + col._index = idx + idx.target = col + return self + + linkml_meta = LinkMLMeta( { "annotations": { @@ -61,7 +297,7 @@ linkml_meta = LinkMLMeta( ) -class VectorData(Data): +class VectorData(VectorDataMixin): """ An n-dimensional dataset representing a column of a DynamicTable. If used without an accompanying VectorIndex, first dimension is along the rows of the DynamicTable and each step along the first dimension is a cell of the larger table. VectorData can also be used to represent a ragged array if paired with a VectorIndex. This allows for storing arrays of varying length in a single cell of the DynamicTable by indexing into this VectorData. The first vector is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], and so on. """ @@ -84,7 +320,7 @@ class VectorData(Data): ] = Field(None) -class VectorIndex(VectorData): +class VectorIndex(VectorIndexMixin): """ Used with VectorData to encode a ragged array. An array of indices into the first dimension of the target VectorData, and forming a map between the rows of a DynamicTable and the indices of the VectorData. The name of the VectorIndex is expected to be the name of the target VectorData object followed by \"_index\". """ @@ -150,7 +386,7 @@ class DynamicTableRegion(VectorData): ] = Field(None) -class DynamicTable(Container): +class DynamicTable(DynamicTableMixin): """ A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). These datasets represent different columns in the table. Apart from a column that contains unique identifiers for each row, there are no other required datasets. Users are free to add any number of custom VectorData objects (columns) here. DynamicTable also supports ragged array columns, where each element can be of a different size. To add a ragged array column, use a VectorIndex type to index the corresponding VectorData type. See documentation for VectorData and VectorIndex for more details. Unlike a compound data type, which is analogous to storing an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable. """ diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/__init__.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/__init__.py @@ -0,0 +1 @@ + diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/hdmf_common_base.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/hdmf_common_base.py new file mode 100644 index 0000000..ad29fbc --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/hdmf_common_base.py @@ -0,0 +1,104 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np + +metamodel_version = "None" +version = "1.5.1" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.base/", + "id": "hdmf-common.base", + "imports": ["hdmf-common.nwb.language"], + "name": "hdmf-common.base", + } +) + + +class Data(ConfiguredBaseModel): + """ + An abstract data type for a dataset. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + name: str = Field(...) + + +class Container(ConfiguredBaseModel): + """ + An abstract data type for a group storing collections of data and metadata. Base type for all data and metadata containers. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + name: str = Field(...) + + +class SimpleMultiContainer(Container): + """ + A simple Container for holding onto multiple containers. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + children: Optional[List[Container]] = Field( + None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "Container"}]}} + ) + name: str = Field(...) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +Data.model_rebuild() +Container.model_rebuild() +SimpleMultiContainer.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/hdmf_common_sparse.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/hdmf_common_sparse.py new file mode 100644 index 0000000..a095b6b --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/hdmf_common_sparse.py @@ -0,0 +1,110 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_5_1.hdmf_common_base import Container +from numpydantic import NDArray, Shape + +metamodel_version = "None" +version = "1.5.1" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +NUMPYDANTIC_VERSION = "1.2.1" +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.sparse/", + "id": "hdmf-common.sparse", + "imports": ["hdmf-common.base", "hdmf-common.nwb.language"], + "name": "hdmf-common.sparse", + } +) + + +class CSRMatrix(Container): + """ + A compressed sparse row matrix. Data are stored in the standard CSR format, where column indices for row i are stored in indices[indptr[i]:indptr[i+1]] and their corresponding values are stored in data[indptr[i]:indptr[i+1]]. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.sparse", "tree_root": True} + ) + + name: str = Field(...) + shape: Optional[np.uint64] = Field( + None, description="""The shape (number of rows, number of columns) of this sparse matrix.""" + ) + indices: NDArray[Shape["* number_of_non_zero_values"], np.uint64] = Field( + ..., + description="""The column indices.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_non_zero_values"}]}} + }, + ) + indptr: NDArray[Shape["* number_of_rows_in_the_matrix_1"], np.uint64] = Field( + ..., + description="""The row index pointer.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_rows_in_the_matrix_1"}]}} + }, + ) + data: CSRMatrixData = Field(..., description="""The non-zero values in the matrix.""") + + +class CSRMatrixData(ConfiguredBaseModel): + """ + The non-zero values in the matrix. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) + + name: Literal["data"] = Field( + "data", + json_schema_extra={"linkml_meta": {"equals_string": "data", "ifabsent": "string(data)"}}, + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +CSRMatrix.model_rebuild() +CSRMatrixData.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/hdmf_common_table.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/hdmf_common_table.py new file mode 100644 index 0000000..91746ee --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/hdmf_common_table.py @@ -0,0 +1,453 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +import numpy as np +from ...hdmf_common.v1_5_1.hdmf_common_base import Data, Container +from pandas import DataFrame +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union, overload, Tuple +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator, model_validator +from numpydantic import NDArray, Shape + +metamodel_version = "None" +version = "1.5.1" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +NUMPYDANTIC_VERSION = "1.2.1" + + +class VectorDataMixin(BaseModel): + """ + Mixin class to give VectorData indexing abilities + """ + + _index: Optional["VectorIndex"] = None + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + + def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + return self._index[item] + else: + return self.array[item] + + def __setitem__(self, key, value) -> None: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class VectorIndexMixin(BaseModel): + """ + Mixin class to give VectorIndex indexing abilities + """ + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + target: Optional["VectorData"] = None + + def _getitem_helper(self, arg: int): + """ + Mimicking :func:`hdmf.common.table.VectorIndex.__getitem_helper` + """ + + start = 0 if arg == 0 else self.array[arg - 1] + end = self.array[arg] + return self.target.array[slice(start, end)] + + def __getitem__(self, item: Union[int, slice]) -> Any: + if self.target is None: + return self.array[item] + elif type(self.target).__name__ == "VectorData": + if isinstance(item, int): + return self._getitem_helper(item) + else: + idx = range(*item.indices(len(self.array))) + return [self._getitem_helper(i) for i in idx] + else: + raise NotImplementedError("DynamicTableRange not supported yet") + + def __setitem__(self, key, value) -> None: + if self._index: + # VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class DynamicTableMixin(BaseModel): + """ + Mixin to make DynamicTable subclasses behave like tables/dataframes + + Mimicking some of the behavior from :class:`hdmf.common.table.DynamicTable` + but simplifying along the way :) + """ + + model_config = ConfigDict(extra="allow") + __pydantic_extra__: Dict[str, Union[list, "NDArray", "VectorData"]] + NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( + "name", + "colnames", + "description", + ) + + # overridden by subclass but implemented here for testing and typechecking purposes :) + colnames: List[str] = Field(default_factory=list) + + @property + def _columns(self) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + return {k: getattr(self, k) for i, k in enumerate(self.colnames)} + + @property + def _columns_list(self) -> List[Union[list, "NDArray", "VectorData"]]: + return [getattr(self, k) for i, k in enumerate(self.colnames)] + + @overload + def __getitem__(self, item: str) -> Union[list, "NDArray", "VectorData"]: ... + + @overload + def __getitem__(self, item: int) -> DataFrame: ... + + @overload + def __getitem__(self, item: Tuple[int, Union[int, str]]) -> Any: ... + + @overload + def __getitem__(self, item: Tuple[Union[int, slice], ...]) -> Union[ + DataFrame, + list, + "NDArray", + "VectorData", + ]: ... + + @overload + def __getitem__(self, item: slice) -> DataFrame: ... + + def __getitem__( + self, + item: Union[ + str, + int, + slice, + Tuple[int, Union[int, str]], + Tuple[Union[int, slice], ...], + ], + ) -> Any: + """ + Get an item from the table + + If item is... + + - ``str`` : get the column with this name + - ``int`` : get the row at this index + - ``tuple[int, int]`` : get a specific cell value eg. (0,1) gets the 0th row and 1st column + - ``tuple[int, str]`` : get a specific cell value eg. (0, 'colname') + gets the 0th row from ``colname`` + - ``tuple[int | slice, int | slice]`` : get a range of cells from a range of columns. + returns as a :class:`pandas.DataFrame` + """ + if isinstance(item, str): + return self._columns[item] + if isinstance(item, (int, slice)): + return DataFrame.from_dict(self._slice_range(item)) + elif isinstance(item, tuple): + if len(item) != 2: + raise ValueError( + "DynamicTables are 2-dimensional, can't index with more than 2 indices like" + f" {item}" + ) + + # all other cases are tuples of (rows, cols) + rows, cols = item + if isinstance(cols, (int, slice)): + cols = self.colnames[cols] + data = self._slice_range(rows, cols) + return DataFrame.from_dict(data) + else: + raise ValueError(f"Unsure how to get item with key {item}") + + def _slice_range( + self, rows: Union[int, slice], cols: Optional[Union[str, List[str]]] = None + ) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + if cols is None: + cols = self.colnames + elif isinstance(cols, str): + cols = [cols] + + data = {k: self._columns[k][rows] for k in cols} + return data + + def __setitem__(self, key: str, value: Any) -> None: + raise NotImplementedError("TODO") + + def __setattr__(self, key: str, value: Union[list, "NDArray", "VectorData"]): + """ + Add a column, appending it to ``colnames`` + """ + # don't use this while building the model + if not getattr(self, "__pydantic_complete__", False): + return super().__setattr__(key, value) + + if key not in self.model_fields_set and not key.endswith("_index"): + self.colnames.append(key) + + return super().__setattr__(key, value) + + @model_validator(mode="before") + @classmethod + def create_colnames(cls, model: Dict[str, Any]): + """ + Construct colnames from arguments. + + the model dict is ordered after python3.6, so we can use that minus + anything in :attr:`.NON_COLUMN_FIELDS` to determine order implied from passage order + """ + if "colnames" not in model: + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") + ] + model["colnames"] = colnames + else: + # add any columns not explicitly given an order at the end + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS + and not k.endswith("_index") + and k not in model["colnames"].keys() + ] + model["colnames"].extend(colnames) + return model + + @model_validator(mode="after") + def resolve_targets(self) -> "DynamicTableMixin": + """ + Ensure that any implicitly indexed columns are linked, and create backlinks + """ + for key, col in self._columns.items(): + if isinstance(col, VectorData): + # find an index + idx = None + for field_name in self.model_fields_set: + # implicit name-based index + field = getattr(self, field_name) + if isinstance(field, VectorIndex): + if field_name == f"{key}_index": + idx = field + break + elif field.target is col: + idx = field + break + if idx is not None: + col._index = idx + idx.target = col + return self + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.table/", + "id": "hdmf-common.table", + "imports": ["hdmf-common.base", "hdmf-common.nwb.language"], + "name": "hdmf-common.table", + } +) + + +class VectorData(VectorDataMixin): + """ + An n-dimensional dataset representing a column of a DynamicTable. If used without an accompanying VectorIndex, first dimension is along the rows of the DynamicTable and each step along the first dimension is a cell of the larger table. VectorData can also be used to represent a ragged array if paired with a VectorIndex. This allows for storing arrays of varying length in a single cell of the DynamicTable by indexing into this VectorData. The first vector is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], and so on. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class VectorIndex(VectorIndexMixin): + """ + Used with VectorData to encode a ragged array. An array of indices into the first dimension of the target VectorData, and forming a map between the rows of a DynamicTable and the indices of the VectorData. The name of the VectorIndex is expected to be the name of the target VectorData object followed by \"_index\". + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + target: Optional[VectorData] = Field( + None, description="""Reference to the target dataset that this index applies to.""" + ) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class ElementIdentifiers(Data): + """ + A list of unique identifiers for values within a dataset, e.g. rows of a DynamicTable. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field( + "element_id", json_schema_extra={"linkml_meta": {"ifabsent": "string(element_id)"}} + ) + + +class DynamicTableRegion(VectorData): + """ + DynamicTableRegion provides a link from one table to an index or region of another. The `table` attribute is a link to another `DynamicTable`, indicating which table is referenced, and the data is int(s) indicating the row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to associate rows with repeated meta-data without data duplication. They can also be used to create hierarchical relationships between multiple `DynamicTable`s. `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create ragged references, so a single cell of a `DynamicTable` can reference many rows of another `DynamicTable`. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + table: Optional[DynamicTable] = Field( + None, description="""Reference to the DynamicTable object that this region applies to.""" + ) + description: Optional[str] = Field( + None, description="""Description of what this table region points to.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class DynamicTable(DynamicTableMixin): + """ + A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). These datasets represent different columns in the table. Apart from a column that contains unique identifiers for each row, there are no other required datasets. Users are free to add any number of custom VectorData objects (columns) here. DynamicTable also supports ragged array columns, where each element can be of a different size. To add a ragged array column, use a VectorIndex type to index the corresponding VectorData type. See documentation for VectorData and VectorIndex for more details. Unlike a compound data type, which is analogous to storing an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + colnames: Optional[str] = Field( + None, + description="""The names of the columns in this table. This should be used to specify an order to the columns.""", + ) + description: Optional[str] = Field( + None, description="""Description of what is in this dynamic table.""" + ) + id: NDArray[Shape["* num_rows"], int] = Field( + ..., + description="""Array of unique identifiers for the rows of this dynamic table.""", + json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, + ) + vector_data: Optional[List[VectorData]] = Field( + None, description="""Vector columns, including index columns, of this dynamic table.""" + ) + + +class AlignedDynamicTable(DynamicTable): + """ + DynamicTable container that supports storing a collection of sub-tables. Each sub-table is a DynamicTable itself that is aligned with the main table by row index. I.e., all DynamicTables stored in this group MUST have the same number of rows. This type effectively defines a 2-level table in which the main data is stored in the main table implemented by this type and additional columns of the table are grouped into categories, with each category being represented by a separate DynamicTable stored within the group. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + children: Optional[List[DynamicTable]] = Field( + None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "DynamicTable"}]}} + ) + name: str = Field(...) + colnames: Optional[str] = Field( + None, + description="""The names of the columns in this table. This should be used to specify an order to the columns.""", + ) + description: Optional[str] = Field( + None, description="""Description of what is in this dynamic table.""" + ) + id: NDArray[Shape["* num_rows"], int] = Field( + ..., + description="""Array of unique identifiers for the rows of this dynamic table.""", + json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, + ) + vector_data: Optional[List[VectorData]] = Field( + None, description="""Vector columns, including index columns, of this dynamic table.""" + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +VectorData.model_rebuild() +VectorIndex.model_rebuild() +ElementIdentifiers.model_rebuild() +DynamicTableRegion.model_rebuild() +DynamicTable.model_rebuild() +AlignedDynamicTable.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/namespace.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/namespace.py new file mode 100644 index 0000000..836122e --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_5_1/namespace.py @@ -0,0 +1,78 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_5_1.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_5_1.hdmf_common_base import Data, Container, SimpleMultiContainer +from ...hdmf_common.v1_5_1.hdmf_common_table import ( + VectorData, + VectorIndex, + ElementIdentifiers, + DynamicTableRegion, + DynamicTable, + AlignedDynamicTable, +) + +metamodel_version = "None" +version = "1.5.1" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": True}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common/", + "description": "Common data structures provided by HDMF", + "id": "hdmf-common", + "imports": [ + "hdmf-common.base", + "hdmf-common.table", + "hdmf-common.sparse", + "hdmf-common.nwb.language", + ], + "name": "hdmf-common", + } +) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/__init__.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/__init__.py @@ -0,0 +1 @@ + diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/hdmf_common_base.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/hdmf_common_base.py new file mode 100644 index 0000000..499e6ad --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/hdmf_common_base.py @@ -0,0 +1,104 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np + +metamodel_version = "None" +version = "1.6.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.base/", + "id": "hdmf-common.base", + "imports": ["hdmf-common.nwb.language"], + "name": "hdmf-common.base", + } +) + + +class Data(ConfiguredBaseModel): + """ + An abstract data type for a dataset. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + name: str = Field(...) + + +class Container(ConfiguredBaseModel): + """ + An abstract data type for a group storing collections of data and metadata. Base type for all data and metadata containers. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + name: str = Field(...) + + +class SimpleMultiContainer(Container): + """ + A simple Container for holding onto multiple containers. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + children: Optional[List[Container]] = Field( + None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "Container"}]}} + ) + name: str = Field(...) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +Data.model_rebuild() +Container.model_rebuild() +SimpleMultiContainer.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/hdmf_common_sparse.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/hdmf_common_sparse.py new file mode 100644 index 0000000..0966f74 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/hdmf_common_sparse.py @@ -0,0 +1,110 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_6_0.hdmf_common_base import Container +from numpydantic import NDArray, Shape + +metamodel_version = "None" +version = "1.6.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +NUMPYDANTIC_VERSION = "1.2.1" +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.sparse/", + "id": "hdmf-common.sparse", + "imports": ["hdmf-common.base", "hdmf-common.nwb.language"], + "name": "hdmf-common.sparse", + } +) + + +class CSRMatrix(Container): + """ + A compressed sparse row matrix. Data are stored in the standard CSR format, where column indices for row i are stored in indices[indptr[i]:indptr[i+1]] and their corresponding values are stored in data[indptr[i]:indptr[i+1]]. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.sparse", "tree_root": True} + ) + + name: str = Field(...) + shape: Optional[np.uint64] = Field( + None, description="""The shape (number of rows, number of columns) of this sparse matrix.""" + ) + indices: NDArray[Shape["* number_of_non_zero_values"], np.uint64] = Field( + ..., + description="""The column indices.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_non_zero_values"}]}} + }, + ) + indptr: NDArray[Shape["* number_of_rows_in_the_matrix_1"], np.uint64] = Field( + ..., + description="""The row index pointer.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_rows_in_the_matrix_1"}]}} + }, + ) + data: CSRMatrixData = Field(..., description="""The non-zero values in the matrix.""") + + +class CSRMatrixData(ConfiguredBaseModel): + """ + The non-zero values in the matrix. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) + + name: Literal["data"] = Field( + "data", + json_schema_extra={"linkml_meta": {"equals_string": "data", "ifabsent": "string(data)"}}, + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +CSRMatrix.model_rebuild() +CSRMatrixData.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/hdmf_common_table.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/hdmf_common_table.py new file mode 100644 index 0000000..e0e06bc --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/hdmf_common_table.py @@ -0,0 +1,453 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +import numpy as np +from ...hdmf_common.v1_6_0.hdmf_common_base import Data, Container +from pandas import DataFrame +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union, overload, Tuple +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator, model_validator +from numpydantic import NDArray, Shape + +metamodel_version = "None" +version = "1.6.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +NUMPYDANTIC_VERSION = "1.2.1" + + +class VectorDataMixin(BaseModel): + """ + Mixin class to give VectorData indexing abilities + """ + + _index: Optional["VectorIndex"] = None + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + + def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + return self._index[item] + else: + return self.array[item] + + def __setitem__(self, key, value) -> None: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class VectorIndexMixin(BaseModel): + """ + Mixin class to give VectorIndex indexing abilities + """ + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + target: Optional["VectorData"] = None + + def _getitem_helper(self, arg: int): + """ + Mimicking :func:`hdmf.common.table.VectorIndex.__getitem_helper` + """ + + start = 0 if arg == 0 else self.array[arg - 1] + end = self.array[arg] + return self.target.array[slice(start, end)] + + def __getitem__(self, item: Union[int, slice]) -> Any: + if self.target is None: + return self.array[item] + elif type(self.target).__name__ == "VectorData": + if isinstance(item, int): + return self._getitem_helper(item) + else: + idx = range(*item.indices(len(self.array))) + return [self._getitem_helper(i) for i in idx] + else: + raise NotImplementedError("DynamicTableRange not supported yet") + + def __setitem__(self, key, value) -> None: + if self._index: + # VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class DynamicTableMixin(BaseModel): + """ + Mixin to make DynamicTable subclasses behave like tables/dataframes + + Mimicking some of the behavior from :class:`hdmf.common.table.DynamicTable` + but simplifying along the way :) + """ + + model_config = ConfigDict(extra="allow") + __pydantic_extra__: Dict[str, Union[list, "NDArray", "VectorData"]] + NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( + "name", + "colnames", + "description", + ) + + # overridden by subclass but implemented here for testing and typechecking purposes :) + colnames: List[str] = Field(default_factory=list) + + @property + def _columns(self) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + return {k: getattr(self, k) for i, k in enumerate(self.colnames)} + + @property + def _columns_list(self) -> List[Union[list, "NDArray", "VectorData"]]: + return [getattr(self, k) for i, k in enumerate(self.colnames)] + + @overload + def __getitem__(self, item: str) -> Union[list, "NDArray", "VectorData"]: ... + + @overload + def __getitem__(self, item: int) -> DataFrame: ... + + @overload + def __getitem__(self, item: Tuple[int, Union[int, str]]) -> Any: ... + + @overload + def __getitem__(self, item: Tuple[Union[int, slice], ...]) -> Union[ + DataFrame, + list, + "NDArray", + "VectorData", + ]: ... + + @overload + def __getitem__(self, item: slice) -> DataFrame: ... + + def __getitem__( + self, + item: Union[ + str, + int, + slice, + Tuple[int, Union[int, str]], + Tuple[Union[int, slice], ...], + ], + ) -> Any: + """ + Get an item from the table + + If item is... + + - ``str`` : get the column with this name + - ``int`` : get the row at this index + - ``tuple[int, int]`` : get a specific cell value eg. (0,1) gets the 0th row and 1st column + - ``tuple[int, str]`` : get a specific cell value eg. (0, 'colname') + gets the 0th row from ``colname`` + - ``tuple[int | slice, int | slice]`` : get a range of cells from a range of columns. + returns as a :class:`pandas.DataFrame` + """ + if isinstance(item, str): + return self._columns[item] + if isinstance(item, (int, slice)): + return DataFrame.from_dict(self._slice_range(item)) + elif isinstance(item, tuple): + if len(item) != 2: + raise ValueError( + "DynamicTables are 2-dimensional, can't index with more than 2 indices like" + f" {item}" + ) + + # all other cases are tuples of (rows, cols) + rows, cols = item + if isinstance(cols, (int, slice)): + cols = self.colnames[cols] + data = self._slice_range(rows, cols) + return DataFrame.from_dict(data) + else: + raise ValueError(f"Unsure how to get item with key {item}") + + def _slice_range( + self, rows: Union[int, slice], cols: Optional[Union[str, List[str]]] = None + ) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + if cols is None: + cols = self.colnames + elif isinstance(cols, str): + cols = [cols] + + data = {k: self._columns[k][rows] for k in cols} + return data + + def __setitem__(self, key: str, value: Any) -> None: + raise NotImplementedError("TODO") + + def __setattr__(self, key: str, value: Union[list, "NDArray", "VectorData"]): + """ + Add a column, appending it to ``colnames`` + """ + # don't use this while building the model + if not getattr(self, "__pydantic_complete__", False): + return super().__setattr__(key, value) + + if key not in self.model_fields_set and not key.endswith("_index"): + self.colnames.append(key) + + return super().__setattr__(key, value) + + @model_validator(mode="before") + @classmethod + def create_colnames(cls, model: Dict[str, Any]): + """ + Construct colnames from arguments. + + the model dict is ordered after python3.6, so we can use that minus + anything in :attr:`.NON_COLUMN_FIELDS` to determine order implied from passage order + """ + if "colnames" not in model: + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") + ] + model["colnames"] = colnames + else: + # add any columns not explicitly given an order at the end + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS + and not k.endswith("_index") + and k not in model["colnames"].keys() + ] + model["colnames"].extend(colnames) + return model + + @model_validator(mode="after") + def resolve_targets(self) -> "DynamicTableMixin": + """ + Ensure that any implicitly indexed columns are linked, and create backlinks + """ + for key, col in self._columns.items(): + if isinstance(col, VectorData): + # find an index + idx = None + for field_name in self.model_fields_set: + # implicit name-based index + field = getattr(self, field_name) + if isinstance(field, VectorIndex): + if field_name == f"{key}_index": + idx = field + break + elif field.target is col: + idx = field + break + if idx is not None: + col._index = idx + idx.target = col + return self + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.table/", + "id": "hdmf-common.table", + "imports": ["hdmf-common.base", "hdmf-common.nwb.language"], + "name": "hdmf-common.table", + } +) + + +class VectorData(VectorDataMixin): + """ + An n-dimensional dataset representing a column of a DynamicTable. If used without an accompanying VectorIndex, first dimension is along the rows of the DynamicTable and each step along the first dimension is a cell of the larger table. VectorData can also be used to represent a ragged array if paired with a VectorIndex. This allows for storing arrays of varying length in a single cell of the DynamicTable by indexing into this VectorData. The first vector is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], and so on. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class VectorIndex(VectorIndexMixin): + """ + Used with VectorData to encode a ragged array. An array of indices into the first dimension of the target VectorData, and forming a map between the rows of a DynamicTable and the indices of the VectorData. The name of the VectorIndex is expected to be the name of the target VectorData object followed by \"_index\". + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + target: Optional[VectorData] = Field( + None, description="""Reference to the target dataset that this index applies to.""" + ) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class ElementIdentifiers(Data): + """ + A list of unique identifiers for values within a dataset, e.g. rows of a DynamicTable. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field( + "element_id", json_schema_extra={"linkml_meta": {"ifabsent": "string(element_id)"}} + ) + + +class DynamicTableRegion(VectorData): + """ + DynamicTableRegion provides a link from one table to an index or region of another. The `table` attribute is a link to another `DynamicTable`, indicating which table is referenced, and the data is int(s) indicating the row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to associate rows with repeated meta-data without data duplication. They can also be used to create hierarchical relationships between multiple `DynamicTable`s. `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create ragged references, so a single cell of a `DynamicTable` can reference many rows of another `DynamicTable`. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + table: Optional[DynamicTable] = Field( + None, description="""Reference to the DynamicTable object that this region applies to.""" + ) + description: Optional[str] = Field( + None, description="""Description of what this table region points to.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class DynamicTable(DynamicTableMixin): + """ + A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). These datasets represent different columns in the table. Apart from a column that contains unique identifiers for each row, there are no other required datasets. Users are free to add any number of custom VectorData objects (columns) here. DynamicTable also supports ragged array columns, where each element can be of a different size. To add a ragged array column, use a VectorIndex type to index the corresponding VectorData type. See documentation for VectorData and VectorIndex for more details. Unlike a compound data type, which is analogous to storing an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + colnames: Optional[str] = Field( + None, + description="""The names of the columns in this table. This should be used to specify an order to the columns.""", + ) + description: Optional[str] = Field( + None, description="""Description of what is in this dynamic table.""" + ) + id: NDArray[Shape["* num_rows"], int] = Field( + ..., + description="""Array of unique identifiers for the rows of this dynamic table.""", + json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, + ) + vector_data: Optional[List[VectorData]] = Field( + None, description="""Vector columns, including index columns, of this dynamic table.""" + ) + + +class AlignedDynamicTable(DynamicTable): + """ + DynamicTable container that supports storing a collection of sub-tables. Each sub-table is a DynamicTable itself that is aligned with the main table by row index. I.e., all DynamicTables stored in this group MUST have the same number of rows. This type effectively defines a 2-level table in which the main data is stored in the main table implemented by this type and additional columns of the table are grouped into categories, with each category being represented by a separate DynamicTable stored within the group. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + children: Optional[List[DynamicTable]] = Field( + None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "DynamicTable"}]}} + ) + name: str = Field(...) + colnames: Optional[str] = Field( + None, + description="""The names of the columns in this table. This should be used to specify an order to the columns.""", + ) + description: Optional[str] = Field( + None, description="""Description of what is in this dynamic table.""" + ) + id: NDArray[Shape["* num_rows"], int] = Field( + ..., + description="""Array of unique identifiers for the rows of this dynamic table.""", + json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, + ) + vector_data: Optional[List[VectorData]] = Field( + None, description="""Vector columns, including index columns, of this dynamic table.""" + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +VectorData.model_rebuild() +VectorIndex.model_rebuild() +ElementIdentifiers.model_rebuild() +DynamicTableRegion.model_rebuild() +DynamicTable.model_rebuild() +AlignedDynamicTable.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/namespace.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/namespace.py new file mode 100644 index 0000000..1dc832f --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_6_0/namespace.py @@ -0,0 +1,78 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_6_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_6_0.hdmf_common_base import Data, Container, SimpleMultiContainer +from ...hdmf_common.v1_6_0.hdmf_common_table import ( + VectorData, + VectorIndex, + ElementIdentifiers, + DynamicTableRegion, + DynamicTable, + AlignedDynamicTable, +) + +metamodel_version = "None" +version = "1.6.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": True}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common/", + "description": "Common data structures provided by HDMF", + "id": "hdmf-common", + "imports": [ + "hdmf-common.base", + "hdmf-common.table", + "hdmf-common.sparse", + "hdmf-common.nwb.language", + ], + "name": "hdmf-common", + } +) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/__init__.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/__init__.py @@ -0,0 +1 @@ + diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/hdmf_common_base.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/hdmf_common_base.py new file mode 100644 index 0000000..751693c --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/hdmf_common_base.py @@ -0,0 +1,104 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np + +metamodel_version = "None" +version = "1.7.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.base/", + "id": "hdmf-common.base", + "imports": ["hdmf-common.nwb.language"], + "name": "hdmf-common.base", + } +) + + +class Data(ConfiguredBaseModel): + """ + An abstract data type for a dataset. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + name: str = Field(...) + + +class Container(ConfiguredBaseModel): + """ + An abstract data type for a group storing collections of data and metadata. Base type for all data and metadata containers. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + name: str = Field(...) + + +class SimpleMultiContainer(Container): + """ + A simple Container for holding onto multiple containers. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.base", "tree_root": True} + ) + + children: Optional[List[Container]] = Field( + None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "Container"}]}} + ) + name: str = Field(...) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +Data.model_rebuild() +Container.model_rebuild() +SimpleMultiContainer.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/hdmf_common_sparse.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/hdmf_common_sparse.py new file mode 100644 index 0000000..c6bfab5 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/hdmf_common_sparse.py @@ -0,0 +1,110 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_7_0.hdmf_common_base import Container +from numpydantic import NDArray, Shape + +metamodel_version = "None" +version = "1.7.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +NUMPYDANTIC_VERSION = "1.2.1" +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.sparse/", + "id": "hdmf-common.sparse", + "imports": ["hdmf-common.base", "hdmf-common.nwb.language"], + "name": "hdmf-common.sparse", + } +) + + +class CSRMatrix(Container): + """ + A compressed sparse row matrix. Data are stored in the standard CSR format, where column indices for row i are stored in indices[indptr[i]:indptr[i+1]] and their corresponding values are stored in data[indptr[i]:indptr[i+1]]. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.sparse", "tree_root": True} + ) + + name: str = Field(...) + shape: Optional[np.uint64] = Field( + None, description="""The shape (number of rows, number of columns) of this sparse matrix.""" + ) + indices: NDArray[Shape["* number_of_non_zero_values"], np.uint64] = Field( + ..., + description="""The column indices.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_non_zero_values"}]}} + }, + ) + indptr: NDArray[Shape["* number_of_rows_in_the_matrix_1"], np.uint64] = Field( + ..., + description="""The row index pointer.""", + json_schema_extra={ + "linkml_meta": {"array": {"dimensions": [{"alias": "number_of_rows_in_the_matrix_1"}]}} + }, + ) + data: CSRMatrixData = Field(..., description="""The non-zero values in the matrix.""") + + +class CSRMatrixData(ConfiguredBaseModel): + """ + The non-zero values in the matrix. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-common.sparse"}) + + name: Literal["data"] = Field( + "data", + json_schema_extra={"linkml_meta": {"equals_string": "data", "ifabsent": "string(data)"}}, + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +CSRMatrix.model_rebuild() +CSRMatrixData.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/hdmf_common_table.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/hdmf_common_table.py new file mode 100644 index 0000000..65a9dd3 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/hdmf_common_table.py @@ -0,0 +1,453 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +import numpy as np +from ...hdmf_common.v1_7_0.hdmf_common_base import Data, Container +from pandas import DataFrame +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union, overload, Tuple +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator, model_validator +from numpydantic import NDArray, Shape + +metamodel_version = "None" +version = "1.7.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +NUMPYDANTIC_VERSION = "1.2.1" + + +class VectorDataMixin(BaseModel): + """ + Mixin class to give VectorData indexing abilities + """ + + _index: Optional["VectorIndex"] = None + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + + def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + return self._index[item] + else: + return self.array[item] + + def __setitem__(self, key, value) -> None: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class VectorIndexMixin(BaseModel): + """ + Mixin class to give VectorIndex indexing abilities + """ + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + target: Optional["VectorData"] = None + + def _getitem_helper(self, arg: int): + """ + Mimicking :func:`hdmf.common.table.VectorIndex.__getitem_helper` + """ + + start = 0 if arg == 0 else self.array[arg - 1] + end = self.array[arg] + return self.target.array[slice(start, end)] + + def __getitem__(self, item: Union[int, slice]) -> Any: + if self.target is None: + return self.array[item] + elif type(self.target).__name__ == "VectorData": + if isinstance(item, int): + return self._getitem_helper(item) + else: + idx = range(*item.indices(len(self.array))) + return [self._getitem_helper(i) for i in idx] + else: + raise NotImplementedError("DynamicTableRange not supported yet") + + def __setitem__(self, key, value) -> None: + if self._index: + # VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class DynamicTableMixin(BaseModel): + """ + Mixin to make DynamicTable subclasses behave like tables/dataframes + + Mimicking some of the behavior from :class:`hdmf.common.table.DynamicTable` + but simplifying along the way :) + """ + + model_config = ConfigDict(extra="allow") + __pydantic_extra__: Dict[str, Union[list, "NDArray", "VectorData"]] + NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( + "name", + "colnames", + "description", + ) + + # overridden by subclass but implemented here for testing and typechecking purposes :) + colnames: List[str] = Field(default_factory=list) + + @property + def _columns(self) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + return {k: getattr(self, k) for i, k in enumerate(self.colnames)} + + @property + def _columns_list(self) -> List[Union[list, "NDArray", "VectorData"]]: + return [getattr(self, k) for i, k in enumerate(self.colnames)] + + @overload + def __getitem__(self, item: str) -> Union[list, "NDArray", "VectorData"]: ... + + @overload + def __getitem__(self, item: int) -> DataFrame: ... + + @overload + def __getitem__(self, item: Tuple[int, Union[int, str]]) -> Any: ... + + @overload + def __getitem__(self, item: Tuple[Union[int, slice], ...]) -> Union[ + DataFrame, + list, + "NDArray", + "VectorData", + ]: ... + + @overload + def __getitem__(self, item: slice) -> DataFrame: ... + + def __getitem__( + self, + item: Union[ + str, + int, + slice, + Tuple[int, Union[int, str]], + Tuple[Union[int, slice], ...], + ], + ) -> Any: + """ + Get an item from the table + + If item is... + + - ``str`` : get the column with this name + - ``int`` : get the row at this index + - ``tuple[int, int]`` : get a specific cell value eg. (0,1) gets the 0th row and 1st column + - ``tuple[int, str]`` : get a specific cell value eg. (0, 'colname') + gets the 0th row from ``colname`` + - ``tuple[int | slice, int | slice]`` : get a range of cells from a range of columns. + returns as a :class:`pandas.DataFrame` + """ + if isinstance(item, str): + return self._columns[item] + if isinstance(item, (int, slice)): + return DataFrame.from_dict(self._slice_range(item)) + elif isinstance(item, tuple): + if len(item) != 2: + raise ValueError( + "DynamicTables are 2-dimensional, can't index with more than 2 indices like" + f" {item}" + ) + + # all other cases are tuples of (rows, cols) + rows, cols = item + if isinstance(cols, (int, slice)): + cols = self.colnames[cols] + data = self._slice_range(rows, cols) + return DataFrame.from_dict(data) + else: + raise ValueError(f"Unsure how to get item with key {item}") + + def _slice_range( + self, rows: Union[int, slice], cols: Optional[Union[str, List[str]]] = None + ) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + if cols is None: + cols = self.colnames + elif isinstance(cols, str): + cols = [cols] + + data = {k: self._columns[k][rows] for k in cols} + return data + + def __setitem__(self, key: str, value: Any) -> None: + raise NotImplementedError("TODO") + + def __setattr__(self, key: str, value: Union[list, "NDArray", "VectorData"]): + """ + Add a column, appending it to ``colnames`` + """ + # don't use this while building the model + if not getattr(self, "__pydantic_complete__", False): + return super().__setattr__(key, value) + + if key not in self.model_fields_set and not key.endswith("_index"): + self.colnames.append(key) + + return super().__setattr__(key, value) + + @model_validator(mode="before") + @classmethod + def create_colnames(cls, model: Dict[str, Any]): + """ + Construct colnames from arguments. + + the model dict is ordered after python3.6, so we can use that minus + anything in :attr:`.NON_COLUMN_FIELDS` to determine order implied from passage order + """ + if "colnames" not in model: + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") + ] + model["colnames"] = colnames + else: + # add any columns not explicitly given an order at the end + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS + and not k.endswith("_index") + and k not in model["colnames"].keys() + ] + model["colnames"].extend(colnames) + return model + + @model_validator(mode="after") + def resolve_targets(self) -> "DynamicTableMixin": + """ + Ensure that any implicitly indexed columns are linked, and create backlinks + """ + for key, col in self._columns.items(): + if isinstance(col, VectorData): + # find an index + idx = None + for field_name in self.model_fields_set: + # implicit name-based index + field = getattr(self, field_name) + if isinstance(field, VectorIndex): + if field_name == f"{key}_index": + idx = field + break + elif field.target is col: + idx = field + break + if idx is not None: + col._index = idx + idx.target = col + return self + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common.table/", + "id": "hdmf-common.table", + "imports": ["hdmf-common.base", "hdmf-common.nwb.language"], + "name": "hdmf-common.table", + } +) + + +class VectorData(VectorDataMixin): + """ + An n-dimensional dataset representing a column of a DynamicTable. If used without an accompanying VectorIndex, first dimension is along the rows of the DynamicTable and each step along the first dimension is a cell of the larger table. VectorData can also be used to represent a ragged array if paired with a VectorIndex. This allows for storing arrays of varying length in a single cell of the DynamicTable by indexing into this VectorData. The first vector is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], and so on. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class VectorIndex(VectorIndexMixin): + """ + Used with VectorData to encode a ragged array. An array of indices into the first dimension of the target VectorData, and forming a map between the rows of a DynamicTable and the indices of the VectorData. The name of the VectorIndex is expected to be the name of the target VectorData object followed by \"_index\". + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + target: Optional[VectorData] = Field( + None, description="""Reference to the target dataset that this index applies to.""" + ) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class ElementIdentifiers(Data): + """ + A list of unique identifiers for values within a dataset, e.g. rows of a DynamicTable. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field( + "element_id", json_schema_extra={"linkml_meta": {"ifabsent": "string(element_id)"}} + ) + + +class DynamicTableRegion(VectorData): + """ + DynamicTableRegion provides a link from one table to an index or region of another. The `table` attribute is a link to another `DynamicTable`, indicating which table is referenced, and the data is int(s) indicating the row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to associate rows with repeated meta-data without data duplication. They can also be used to create hierarchical relationships between multiple `DynamicTable`s. `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create ragged references, so a single cell of a `DynamicTable` can reference many rows of another `DynamicTable`. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + table: Optional[DynamicTable] = Field( + None, description="""Reference to the DynamicTable object that this region applies to.""" + ) + description: Optional[str] = Field( + None, description="""Description of what this table region points to.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +class DynamicTable(DynamicTableMixin): + """ + A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). These datasets represent different columns in the table. Apart from a column that contains unique identifiers for each row, there are no other required datasets. Users are free to add any number of custom VectorData objects (columns) here. DynamicTable also supports ragged array columns, where each element can be of a different size. To add a ragged array column, use a VectorIndex type to index the corresponding VectorData type. See documentation for VectorData and VectorIndex for more details. Unlike a compound data type, which is analogous to storing an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + name: str = Field(...) + colnames: Optional[str] = Field( + None, + description="""The names of the columns in this table. This should be used to specify an order to the columns.""", + ) + description: Optional[str] = Field( + None, description="""Description of what is in this dynamic table.""" + ) + id: NDArray[Shape["* num_rows"], int] = Field( + ..., + description="""Array of unique identifiers for the rows of this dynamic table.""", + json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, + ) + vector_data: Optional[List[VectorData]] = Field( + None, description="""Vector columns, including index columns, of this dynamic table.""" + ) + + +class AlignedDynamicTable(DynamicTable): + """ + DynamicTable container that supports storing a collection of sub-tables. Each sub-table is a DynamicTable itself that is aligned with the main table by row index. I.e., all DynamicTables stored in this group MUST have the same number of rows. This type effectively defines a 2-level table in which the main data is stored in the main table implemented by this type and additional columns of the table are grouped into categories, with each category being represented by a separate DynamicTable stored within the group. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-common.table", "tree_root": True} + ) + + children: Optional[List[DynamicTable]] = Field( + None, json_schema_extra={"linkml_meta": {"any_of": [{"range": "DynamicTable"}]}} + ) + name: str = Field(...) + colnames: Optional[str] = Field( + None, + description="""The names of the columns in this table. This should be used to specify an order to the columns.""", + ) + description: Optional[str] = Field( + None, description="""Description of what is in this dynamic table.""" + ) + id: NDArray[Shape["* num_rows"], int] = Field( + ..., + description="""Array of unique identifiers for the rows of this dynamic table.""", + json_schema_extra={"linkml_meta": {"array": {"dimensions": [{"alias": "num_rows"}]}}}, + ) + vector_data: Optional[List[VectorData]] = Field( + None, description="""Vector columns, including index columns, of this dynamic table.""" + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +VectorData.model_rebuild() +VectorIndex.model_rebuild() +ElementIdentifiers.model_rebuild() +DynamicTableRegion.model_rebuild() +DynamicTable.model_rebuild() +AlignedDynamicTable.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/namespace.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/namespace.py new file mode 100644 index 0000000..7d70e39 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_7_0/namespace.py @@ -0,0 +1,78 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_7_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_7_0.hdmf_common_base import Data, Container, SimpleMultiContainer +from ...hdmf_common.v1_7_0.hdmf_common_table import ( + VectorData, + VectorIndex, + ElementIdentifiers, + DynamicTableRegion, + DynamicTable, + AlignedDynamicTable, +) + +metamodel_version = "None" +version = "1.7.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": True}, + "namespace": {"tag": "namespace", "value": "hdmf-common"}, + }, + "default_prefix": "hdmf-common/", + "description": "Common data structures provided by HDMF", + "id": "hdmf-common", + "imports": [ + "hdmf-common.base", + "hdmf-common.table", + "hdmf-common.sparse", + "hdmf-common.nwb.language", + ], + "name": "hdmf-common", + } +) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_8_0/hdmf_common_table.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_8_0/hdmf_common_table.py index 46ad6ef..986c628 100644 --- a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_8_0/hdmf_common_table.py +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_common/v1_8_0/hdmf_common_table.py @@ -4,10 +4,11 @@ from decimal import Decimal from enum import Enum import re import sys -from typing import Any, ClassVar, List, Literal, Dict, Optional, Union -from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator import numpy as np from ...hdmf_common.v1_8_0.hdmf_common_base import Data, Container +from pandas import DataFrame +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union, overload, Tuple +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator, model_validator from numpydantic import NDArray, Shape metamodel_version = "None" @@ -47,6 +48,241 @@ class LinkMLMeta(RootModel): NUMPYDANTIC_VERSION = "1.2.1" + + +class VectorDataMixin(BaseModel): + """ + Mixin class to give VectorData indexing abilities + """ + + _index: Optional["VectorIndex"] = None + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + + def __getitem__(self, item: Union[str, int, slice, Tuple[Union[str, int, slice], ...]]) -> Any: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + return self._index[item] + else: + return self.array[item] + + def __setitem__(self, key, value) -> None: + if self._index: + # Following hdmf, VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class VectorIndexMixin(BaseModel): + """ + Mixin class to give VectorIndex indexing abilities + """ + + # redefined in `VectorData`, but included here for testing and type checking + array: Optional[NDArray] = None + target: Optional["VectorData"] = None + + def _getitem_helper(self, arg: int): + """ + Mimicking :func:`hdmf.common.table.VectorIndex.__getitem_helper` + """ + + start = 0 if arg == 0 else self.array[arg - 1] + end = self.array[arg] + return self.target.array[slice(start, end)] + + def __getitem__(self, item: Union[int, slice]) -> Any: + if self.target is None: + return self.array[item] + elif type(self.target).__name__ == "VectorData": + if isinstance(item, int): + return self._getitem_helper(item) + else: + idx = range(*item.indices(len(self.array))) + return [self._getitem_helper(i) for i in idx] + else: + raise NotImplementedError("DynamicTableRange not supported yet") + + def __setitem__(self, key, value) -> None: + if self._index: + # VectorIndex is the thing that knows how to do the slicing + self._index[key] = value + else: + self.array[key] = value + + +class DynamicTableMixin(BaseModel): + """ + Mixin to make DynamicTable subclasses behave like tables/dataframes + + Mimicking some of the behavior from :class:`hdmf.common.table.DynamicTable` + but simplifying along the way :) + """ + + model_config = ConfigDict(extra="allow") + __pydantic_extra__: Dict[str, Union[list, "NDArray", "VectorData"]] + NON_COLUMN_FIELDS: ClassVar[tuple[str]] = ( + "name", + "colnames", + "description", + ) + + # overridden by subclass but implemented here for testing and typechecking purposes :) + colnames: List[str] = Field(default_factory=list) + + @property + def _columns(self) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + return {k: getattr(self, k) for i, k in enumerate(self.colnames)} + + @property + def _columns_list(self) -> List[Union[list, "NDArray", "VectorData"]]: + return [getattr(self, k) for i, k in enumerate(self.colnames)] + + @overload + def __getitem__(self, item: str) -> Union[list, "NDArray", "VectorData"]: ... + + @overload + def __getitem__(self, item: int) -> DataFrame: ... + + @overload + def __getitem__(self, item: Tuple[int, Union[int, str]]) -> Any: ... + + @overload + def __getitem__(self, item: Tuple[Union[int, slice], ...]) -> Union[ + DataFrame, + list, + "NDArray", + "VectorData", + ]: ... + + @overload + def __getitem__(self, item: slice) -> DataFrame: ... + + def __getitem__( + self, + item: Union[ + str, + int, + slice, + Tuple[int, Union[int, str]], + Tuple[Union[int, slice], ...], + ], + ) -> Any: + """ + Get an item from the table + + If item is... + + - ``str`` : get the column with this name + - ``int`` : get the row at this index + - ``tuple[int, int]`` : get a specific cell value eg. (0,1) gets the 0th row and 1st column + - ``tuple[int, str]`` : get a specific cell value eg. (0, 'colname') + gets the 0th row from ``colname`` + - ``tuple[int | slice, int | slice]`` : get a range of cells from a range of columns. + returns as a :class:`pandas.DataFrame` + """ + if isinstance(item, str): + return self._columns[item] + if isinstance(item, (int, slice)): + return DataFrame.from_dict(self._slice_range(item)) + elif isinstance(item, tuple): + if len(item) != 2: + raise ValueError( + "DynamicTables are 2-dimensional, can't index with more than 2 indices like" + f" {item}" + ) + + # all other cases are tuples of (rows, cols) + rows, cols = item + if isinstance(cols, (int, slice)): + cols = self.colnames[cols] + data = self._slice_range(rows, cols) + return DataFrame.from_dict(data) + else: + raise ValueError(f"Unsure how to get item with key {item}") + + def _slice_range( + self, rows: Union[int, slice], cols: Optional[Union[str, List[str]]] = None + ) -> Dict[str, Union[list, "NDArray", "VectorData"]]: + if cols is None: + cols = self.colnames + elif isinstance(cols, str): + cols = [cols] + + data = {k: self._columns[k][rows] for k in cols} + return data + + def __setitem__(self, key: str, value: Any) -> None: + raise NotImplementedError("TODO") + + def __setattr__(self, key: str, value: Union[list, "NDArray", "VectorData"]): + """ + Add a column, appending it to ``colnames`` + """ + # don't use this while building the model + if not getattr(self, "__pydantic_complete__", False): + return super().__setattr__(key, value) + + if key not in self.model_fields_set and not key.endswith("_index"): + self.colnames.append(key) + + return super().__setattr__(key, value) + + @model_validator(mode="before") + @classmethod + def create_colnames(cls, model: Dict[str, Any]): + """ + Construct colnames from arguments. + + the model dict is ordered after python3.6, so we can use that minus + anything in :attr:`.NON_COLUMN_FIELDS` to determine order implied from passage order + """ + if "colnames" not in model: + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS and not k.endswith("_index") + ] + model["colnames"] = colnames + else: + # add any columns not explicitly given an order at the end + colnames = [ + k + for k in model.keys() + if k not in cls.NON_COLUMN_FIELDS + and not k.endswith("_index") + and k not in model["colnames"].keys() + ] + model["colnames"].extend(colnames) + return model + + @model_validator(mode="after") + def resolve_targets(self) -> "DynamicTableMixin": + """ + Ensure that any implicitly indexed columns are linked, and create backlinks + """ + for key, col in self._columns.items(): + if isinstance(col, VectorData): + # find an index + idx = None + for field_name in self.model_fields_set: + # implicit name-based index + field = getattr(self, field_name) + if isinstance(field, VectorIndex): + if field_name == f"{key}_index": + idx = field + break + elif field.target is col: + idx = field + break + if idx is not None: + col._index = idx + idx.target = col + return self + + linkml_meta = LinkMLMeta( { "annotations": { @@ -61,7 +297,7 @@ linkml_meta = LinkMLMeta( ) -class VectorData(Data): +class VectorData(VectorDataMixin): """ An n-dimensional dataset representing a column of a DynamicTable. If used without an accompanying VectorIndex, first dimension is along the rows of the DynamicTable and each step along the first dimension is a cell of the larger table. VectorData can also be used to represent a ragged array if paired with a VectorIndex. This allows for storing arrays of varying length in a single cell of the DynamicTable by indexing into this VectorData. The first vector is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], and so on. """ @@ -84,7 +320,7 @@ class VectorData(Data): ] = Field(None) -class VectorIndex(VectorData): +class VectorIndex(VectorIndexMixin): """ Used with VectorData to encode a ragged array. An array of indices into the first dimension of the target VectorData, and forming a map between the rows of a DynamicTable and the indices of the VectorData. The name of the VectorIndex is expected to be the name of the target VectorData object followed by \"_index\". """ @@ -150,7 +386,7 @@ class DynamicTableRegion(VectorData): ] = Field(None) -class DynamicTable(Container): +class DynamicTable(DynamicTableMixin): """ A group containing multiple datasets that are aligned on the first dimension (Currently, this requirement if left up to APIs to check and enforce). These datasets represent different columns in the table. Apart from a column that contains unique identifiers for each row, there are no other required datasets. Users are free to add any number of custom VectorData objects (columns) here. DynamicTable also supports ragged array columns, where each element can be of a different size. To add a ragged array column, use a VectorIndex type to index the corresponding VectorData type. See documentation for VectorData and VectorIndex for more details. Unlike a compound data type, which is analogous to storing an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. This provides an alternative structure to choose from when optimizing storage for anticipated access patterns. Additionally, this type provides a way of creating a table without having to define a compound type up front. Although this convenience may be attractive, users should think carefully about how data will be accessed. DynamicTable is more appropriate for column-centric access, whereas a dataset with a compound type would be more appropriate for row-centric access. Finally, data size should also be taken into account. For small tables, performance loss may be an acceptable trade-off for the flexibility of a DynamicTable. """ diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_experimental.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_experimental.py index 065f135..0ca8353 100644 --- a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_experimental.py +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_experimental.py @@ -7,7 +7,7 @@ import sys from typing import Any, ClassVar, List, Literal, Dict, Optional, Union from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator import numpy as np -from ...hdmf_common.v1_5_0.hdmf_common_table import VectorData +from ...hdmf_common.v1_4_0.hdmf_common_table import VectorData from numpydantic import NDArray, Shape metamodel_version = "None" @@ -55,7 +55,7 @@ linkml_meta = LinkMLMeta( }, "default_prefix": "hdmf-experimental.experimental/", "id": "hdmf-experimental.experimental", - "imports": ["../../hdmf_common/v1_5_0/namespace", "hdmf-experimental.nwb.language"], + "imports": ["../../hdmf_common/v1_4_0/namespace", "hdmf-experimental.nwb.language"], "name": "hdmf-experimental.experimental", } ) diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_resources.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_resources.py index db8a186..affdb23 100644 --- a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_resources.py +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_1_0/hdmf_experimental_resources.py @@ -7,7 +7,7 @@ import sys from typing import Any, ClassVar, List, Literal, Dict, Optional, Union from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator import numpy as np -from ...hdmf_common.v1_5_0.hdmf_common_base import Container, Data +from ...hdmf_common.v1_4_0.hdmf_common_base import Container, Data metamodel_version = "None" version = "0.1.0" @@ -53,7 +53,7 @@ linkml_meta = LinkMLMeta( }, "default_prefix": "hdmf-experimental.resources/", "id": "hdmf-experimental.resources", - "imports": ["../../hdmf_common/v1_5_0/namespace", "hdmf-experimental.nwb.language"], + "imports": ["../../hdmf_common/v1_4_0/namespace", "hdmf-experimental.nwb.language"], "name": "hdmf-experimental.resources", } ) diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_1_0/namespace.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_1_0/namespace.py index 69ffad1..7ea10f7 100644 --- a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_1_0/namespace.py +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_1_0/namespace.py @@ -15,15 +15,14 @@ from ...hdmf_experimental.v0_1_0.hdmf_experimental_resources import ( ExternalResourcesObjects, ExternalResourcesObjectKeys, ) -from ...hdmf_common.v1_5_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData -from ...hdmf_common.v1_5_0.hdmf_common_base import Data, Container, SimpleMultiContainer -from ...hdmf_common.v1_5_0.hdmf_common_table import ( +from ...hdmf_common.v1_4_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_4_0.hdmf_common_base import Data, Container, SimpleMultiContainer +from ...hdmf_common.v1_4_0.hdmf_common_table import ( VectorData, VectorIndex, ElementIdentifiers, DynamicTableRegion, DynamicTable, - AlignedDynamicTable, ) from ...hdmf_experimental.v0_1_0.hdmf_experimental_experimental import EnumData diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/__init__.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/__init__.py @@ -0,0 +1 @@ + diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_experimental.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_experimental.py new file mode 100644 index 0000000..7fef3e3 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_experimental.py @@ -0,0 +1,93 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_5_1.hdmf_common_table import VectorData +from numpydantic import NDArray, Shape + +metamodel_version = "None" +version = "0.2.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +NUMPYDANTIC_VERSION = "1.2.1" +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-experimental"}, + }, + "default_prefix": "hdmf-experimental.experimental/", + "id": "hdmf-experimental.experimental", + "imports": ["../../hdmf_common/v1_5_1/namespace", "hdmf-experimental.nwb.language"], + "name": "hdmf-experimental.experimental", + } +) + + +class EnumData(VectorData): + """ + Data that come from a fixed set of values. A data value of i corresponds to the i-th value in the VectorData referenced by the 'elements' attribute. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-experimental.experimental", "tree_root": True} + ) + + name: str = Field(...) + elements: Optional[VectorData] = Field( + None, + description="""Reference to the VectorData object that contains the enumerable elements""", + ) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +EnumData.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_resources.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_resources.py new file mode 100644 index 0000000..7606660 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/hdmf_experimental_resources.py @@ -0,0 +1,199 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_5_1.hdmf_common_base import Container, Data + +metamodel_version = "None" +version = "0.2.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-experimental"}, + }, + "default_prefix": "hdmf-experimental.resources/", + "id": "hdmf-experimental.resources", + "imports": ["../../hdmf_common/v1_5_1/namespace", "hdmf-experimental.nwb.language"], + "name": "hdmf-experimental.resources", + } +) + + +class ExternalResources(Container): + """ + A set of four tables for tracking external resource references in a file. NOTE: this data type is in beta testing and is subject to change in a later version. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-experimental.resources", "tree_root": True} + ) + + name: str = Field(...) + keys: ExternalResourcesKeys = Field( + ..., + description="""A table for storing user terms that are used to refer to external resources.""", + ) + entities: ExternalResourcesEntities = Field( + ..., description="""A table for mapping user terms (i.e., keys) to resource entities.""" + ) + resources: ExternalResourcesResources = Field( + ..., description="""A table for mapping user terms (i.e., keys) to resource entities.""" + ) + objects: ExternalResourcesObjects = Field( + ..., + description="""A table for identifying which objects in a file contain references to external resources.""", + ) + object_keys: ExternalResourcesObjectKeys = Field( + ..., description="""A table for identifying which objects use which keys.""" + ) + + +class ExternalResourcesKeys(Data): + """ + A table for storing user terms that are used to refer to external resources. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["keys"] = Field( + "keys", + json_schema_extra={"linkml_meta": {"equals_string": "keys", "ifabsent": "string(keys)"}}, + ) + key: str = Field( + ..., + description="""The user term that maps to one or more resources in the 'resources' table.""", + ) + + +class ExternalResourcesEntities(Data): + """ + A table for mapping user terms (i.e., keys) to resource entities. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["entities"] = Field( + "entities", + json_schema_extra={ + "linkml_meta": {"equals_string": "entities", "ifabsent": "string(entities)"} + }, + ) + keys_idx: np.uint64 = Field(..., description="""The index to the key in the 'keys' table.""") + resources_idx: np.uint64 = Field(..., description="""The index into the 'resources' table""") + entity_id: str = Field(..., description="""The unique identifier entity.""") + entity_uri: str = Field( + ..., + description="""The URI for the entity this reference applies to. This can be an empty string.""", + ) + + +class ExternalResourcesResources(Data): + """ + A table for mapping user terms (i.e., keys) to resource entities. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["resources"] = Field( + "resources", + json_schema_extra={ + "linkml_meta": {"equals_string": "resources", "ifabsent": "string(resources)"} + }, + ) + resource: str = Field(..., description="""The name of the resource.""") + resource_uri: str = Field( + ..., description="""The URI for the resource. This can be an empty string.""" + ) + + +class ExternalResourcesObjects(Data): + """ + A table for identifying which objects in a file contain references to external resources. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["objects"] = Field( + "objects", + json_schema_extra={ + "linkml_meta": {"equals_string": "objects", "ifabsent": "string(objects)"} + }, + ) + object_id: str = Field(..., description="""The UUID for the object.""") + relative_path: str = Field( + ..., + description="""The relative path from the container with the object_id to the dataset or attribute with the value(s) that is associated with an external resource. This can be an empty string if the container is a dataset which contains the value(s) that is associated with an external resource.""", + ) + field: str = Field( + ..., + description="""The field of the compound data type using an external resource. This is used only if the dataset or attribute is a compound data type; otherwise this should be an empty string.""", + ) + + +class ExternalResourcesObjectKeys(Data): + """ + A table for identifying which objects use which keys. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["object_keys"] = Field( + "object_keys", + json_schema_extra={ + "linkml_meta": {"equals_string": "object_keys", "ifabsent": "string(object_keys)"} + }, + ) + objects_idx: np.uint64 = Field( + ..., description="""The index to the 'objects' table for the object that holds the key.""" + ) + keys_idx: np.uint64 = Field(..., description="""The index to the 'keys' table for the key.""") + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +ExternalResources.model_rebuild() +ExternalResourcesKeys.model_rebuild() +ExternalResourcesEntities.model_rebuild() +ExternalResourcesResources.model_rebuild() +ExternalResourcesObjects.model_rebuild() +ExternalResourcesObjectKeys.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/namespace.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/namespace.py new file mode 100644 index 0000000..1345536 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_2_0/namespace.py @@ -0,0 +1,89 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_experimental.v0_2_0.hdmf_experimental_resources import ( + ExternalResources, + ExternalResourcesKeys, + ExternalResourcesEntities, + ExternalResourcesResources, + ExternalResourcesObjects, + ExternalResourcesObjectKeys, +) +from ...hdmf_common.v1_5_1.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_5_1.hdmf_common_base import Data, Container, SimpleMultiContainer +from ...hdmf_common.v1_5_1.hdmf_common_table import ( + VectorData, + VectorIndex, + ElementIdentifiers, + DynamicTableRegion, + DynamicTable, + AlignedDynamicTable, +) +from ...hdmf_experimental.v0_2_0.hdmf_experimental_experimental import EnumData + +metamodel_version = "None" +version = "0.2.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": True}, + "namespace": {"tag": "namespace", "value": "hdmf-experimental"}, + }, + "default_prefix": "hdmf-experimental/", + "description": ( + "Experimental data structures provided by HDMF. These are not " + "guaranteed to be available in the future." + ), + "id": "hdmf-experimental", + "imports": [ + "hdmf-experimental.experimental", + "hdmf-experimental.resources", + "hdmf-experimental.nwb.language", + ], + "name": "hdmf-experimental", + } +) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/__init__.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/__init__.py @@ -0,0 +1 @@ + diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_experimental.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_experimental.py new file mode 100644 index 0000000..f57439f --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_experimental.py @@ -0,0 +1,93 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_6_0.hdmf_common_table import VectorData +from numpydantic import NDArray, Shape + +metamodel_version = "None" +version = "0.3.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +NUMPYDANTIC_VERSION = "1.2.1" +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-experimental"}, + }, + "default_prefix": "hdmf-experimental.experimental/", + "id": "hdmf-experimental.experimental", + "imports": ["../../hdmf_common/v1_6_0/namespace", "hdmf-experimental.nwb.language"], + "name": "hdmf-experimental.experimental", + } +) + + +class EnumData(VectorData): + """ + Data that come from a fixed set of values. A data value of i corresponds to the i-th value in the VectorData referenced by the 'elements' attribute. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-experimental.experimental", "tree_root": True} + ) + + name: str = Field(...) + elements: Optional[VectorData] = Field( + None, + description="""Reference to the VectorData object that contains the enumerable elements""", + ) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +EnumData.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_resources.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_resources.py new file mode 100644 index 0000000..600eb18 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/hdmf_experimental_resources.py @@ -0,0 +1,207 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_6_0.hdmf_common_base import Container, Data + +metamodel_version = "None" +version = "0.3.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-experimental"}, + }, + "default_prefix": "hdmf-experimental.resources/", + "id": "hdmf-experimental.resources", + "imports": ["../../hdmf_common/v1_6_0/namespace", "hdmf-experimental.nwb.language"], + "name": "hdmf-experimental.resources", + } +) + + +class ExternalResources(Container): + """ + A set of five tables for tracking external resource references in a file. NOTE: this data type is experimental and is subject to change in a later version. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-experimental.resources", "tree_root": True} + ) + + name: str = Field(...) + keys: ExternalResourcesKeys = Field( + ..., + description="""A table for storing user terms that are used to refer to external resources.""", + ) + files: ExternalResourcesFiles = Field( + ..., description="""A table for storing object ids of files used in external resources.""" + ) + entities: ExternalResourcesEntities = Field( + ..., description="""A table for mapping user terms (i.e., keys) to resource entities.""" + ) + objects: ExternalResourcesObjects = Field( + ..., + description="""A table for identifying which objects in a file contain references to external resources.""", + ) + object_keys: ExternalResourcesObjectKeys = Field( + ..., description="""A table for identifying which objects use which keys.""" + ) + + +class ExternalResourcesKeys(Data): + """ + A table for storing user terms that are used to refer to external resources. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["keys"] = Field( + "keys", + json_schema_extra={"linkml_meta": {"equals_string": "keys", "ifabsent": "string(keys)"}}, + ) + key: str = Field( + ..., + description="""The user term that maps to one or more resources in the `resources` table, e.g., \"human\".""", + ) + + +class ExternalResourcesFiles(Data): + """ + A table for storing object ids of files used in external resources. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["files"] = Field( + "files", + json_schema_extra={"linkml_meta": {"equals_string": "files", "ifabsent": "string(files)"}}, + ) + file_object_id: str = Field( + ..., + description="""The object id (UUID) of a file that contains objects that refers to external resources.""", + ) + + +class ExternalResourcesEntities(Data): + """ + A table for mapping user terms (i.e., keys) to resource entities. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["entities"] = Field( + "entities", + json_schema_extra={ + "linkml_meta": {"equals_string": "entities", "ifabsent": "string(entities)"} + }, + ) + keys_idx: np.uint64 = Field( + ..., description="""The row index to the key in the `keys` table.""" + ) + entity_id: str = Field( + ..., + description="""The compact uniform resource identifier (CURIE) of the entity, in the form [prefix]:[unique local identifier], e.g., 'NCBI_TAXON:9606'.""", + ) + entity_uri: str = Field( + ..., + description="""The URI for the entity this reference applies to. This can be an empty string. e.g., https://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?mode=info&id=9606""", + ) + + +class ExternalResourcesObjects(Data): + """ + A table for identifying which objects in a file contain references to external resources. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["objects"] = Field( + "objects", + json_schema_extra={ + "linkml_meta": {"equals_string": "objects", "ifabsent": "string(objects)"} + }, + ) + files_idx: np.uint64 = Field( + ..., description="""The row index to the file in the `files` table containing the object.""" + ) + object_id: str = Field(..., description="""The object id (UUID) of the object.""") + object_type: str = Field(..., description="""The data type of the object.""") + relative_path: str = Field( + ..., + description="""The relative path from the data object with the `object_id` to the dataset or attribute with the value(s) that is associated with an external resource. This can be an empty string if the object is a dataset that contains the value(s) that is associated with an external resource.""", + ) + field: str = Field( + ..., + description="""The field within the compound data type using an external resource. This is used only if the dataset or attribute is a compound data type; otherwise this should be an empty string.""", + ) + + +class ExternalResourcesObjectKeys(Data): + """ + A table for identifying which objects use which keys. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["object_keys"] = Field( + "object_keys", + json_schema_extra={ + "linkml_meta": {"equals_string": "object_keys", "ifabsent": "string(object_keys)"} + }, + ) + objects_idx: np.uint64 = Field( + ..., description="""The row index to the object in the `objects` table that holds the key""" + ) + keys_idx: np.uint64 = Field( + ..., description="""The row index to the key in the `keys` table.""" + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +ExternalResources.model_rebuild() +ExternalResourcesKeys.model_rebuild() +ExternalResourcesFiles.model_rebuild() +ExternalResourcesEntities.model_rebuild() +ExternalResourcesObjects.model_rebuild() +ExternalResourcesObjectKeys.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/namespace.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/namespace.py new file mode 100644 index 0000000..8361004 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_3_0/namespace.py @@ -0,0 +1,89 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_experimental.v0_3_0.hdmf_experimental_resources import ( + ExternalResources, + ExternalResourcesKeys, + ExternalResourcesFiles, + ExternalResourcesEntities, + ExternalResourcesObjects, + ExternalResourcesObjectKeys, +) +from ...hdmf_common.v1_6_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_6_0.hdmf_common_base import Data, Container, SimpleMultiContainer +from ...hdmf_common.v1_6_0.hdmf_common_table import ( + VectorData, + VectorIndex, + ElementIdentifiers, + DynamicTableRegion, + DynamicTable, + AlignedDynamicTable, +) +from ...hdmf_experimental.v0_3_0.hdmf_experimental_experimental import EnumData + +metamodel_version = "None" +version = "0.3.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": True}, + "namespace": {"tag": "namespace", "value": "hdmf-experimental"}, + }, + "default_prefix": "hdmf-experimental/", + "description": ( + "Experimental data structures provided by HDMF. These are not " + "guaranteed to be available in the future." + ), + "id": "hdmf-experimental", + "imports": [ + "hdmf-experimental.experimental", + "hdmf-experimental.resources", + "hdmf-experimental.nwb.language", + ], + "name": "hdmf-experimental", + } +) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/__init__.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/__init__.py @@ -0,0 +1 @@ + diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_experimental.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_experimental.py new file mode 100644 index 0000000..14e4d1a --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_experimental.py @@ -0,0 +1,93 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_7_0.hdmf_common_table import VectorData +from numpydantic import NDArray, Shape + +metamodel_version = "None" +version = "0.4.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +NUMPYDANTIC_VERSION = "1.2.1" +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-experimental"}, + }, + "default_prefix": "hdmf-experimental.experimental/", + "id": "hdmf-experimental.experimental", + "imports": ["../../hdmf_common/v1_7_0/namespace", "hdmf-experimental.nwb.language"], + "name": "hdmf-experimental.experimental", + } +) + + +class EnumData(VectorData): + """ + Data that come from a fixed set of values. A data value of i corresponds to the i-th value in the VectorData referenced by the 'elements' attribute. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-experimental.experimental", "tree_root": True} + ) + + name: str = Field(...) + elements: Optional[VectorData] = Field( + None, + description="""Reference to the VectorData object that contains the enumerable elements""", + ) + description: Optional[str] = Field( + None, description="""Description of what these vectors represent.""" + ) + array: Optional[ + Union[ + NDArray[Shape["* dim0"], Any], + NDArray[Shape["* dim0, * dim1"], Any], + NDArray[Shape["* dim0, * dim1, * dim2"], Any], + NDArray[Shape["* dim0, * dim1, * dim2, * dim3"], Any], + ] + ] = Field(None) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +EnumData.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_resources.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_resources.py new file mode 100644 index 0000000..6279463 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/hdmf_experimental_resources.py @@ -0,0 +1,229 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_common.v1_7_0.hdmf_common_base import Container, Data + +metamodel_version = "None" +version = "0.4.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": False}, + "namespace": {"tag": "namespace", "value": "hdmf-experimental"}, + }, + "default_prefix": "hdmf-experimental.resources/", + "id": "hdmf-experimental.resources", + "imports": ["../../hdmf_common/v1_7_0/namespace", "hdmf-experimental.nwb.language"], + "name": "hdmf-experimental.resources", + } +) + + +class ExternalResources(Container): + """ + A set of five tables for tracking external resource references in a file. NOTE: this data type is experimental and is subject to change in a later version. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta( + {"from_schema": "hdmf-experimental.resources", "tree_root": True} + ) + + name: str = Field(...) + keys: ExternalResourcesKeys = Field( + ..., + description="""A table for storing user terms that are used to refer to external resources.""", + ) + files: ExternalResourcesFiles = Field( + ..., description="""A table for storing object ids of files used in external resources.""" + ) + entities: ExternalResourcesEntities = Field( + ..., description="""A table for mapping user terms (i.e., keys) to resource entities.""" + ) + objects: ExternalResourcesObjects = Field( + ..., + description="""A table for identifying which objects in a file contain references to external resources.""", + ) + object_keys: ExternalResourcesObjectKeys = Field( + ..., description="""A table for identifying which objects use which keys.""" + ) + entity_keys: ExternalResourcesEntityKeys = Field( + ..., description="""A table for identifying which keys use which entity.""" + ) + + +class ExternalResourcesKeys(Data): + """ + A table for storing user terms that are used to refer to external resources. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["keys"] = Field( + "keys", + json_schema_extra={"linkml_meta": {"equals_string": "keys", "ifabsent": "string(keys)"}}, + ) + key: str = Field( + ..., + description="""The user term that maps to one or more resources in the `resources` table, e.g., \"human\".""", + ) + + +class ExternalResourcesFiles(Data): + """ + A table for storing object ids of files used in external resources. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["files"] = Field( + "files", + json_schema_extra={"linkml_meta": {"equals_string": "files", "ifabsent": "string(files)"}}, + ) + file_object_id: str = Field( + ..., + description="""The object id (UUID) of a file that contains objects that refers to external resources.""", + ) + + +class ExternalResourcesEntities(Data): + """ + A table for mapping user terms (i.e., keys) to resource entities. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["entities"] = Field( + "entities", + json_schema_extra={ + "linkml_meta": {"equals_string": "entities", "ifabsent": "string(entities)"} + }, + ) + entity_id: str = Field( + ..., + description="""The compact uniform resource identifier (CURIE) of the entity, in the form [prefix]:[unique local identifier], e.g., 'NCBI_TAXON:9606'.""", + ) + entity_uri: str = Field( + ..., + description="""The URI for the entity this reference applies to. This can be an empty string. e.g., https://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?mode=info&id=9606""", + ) + + +class ExternalResourcesObjects(Data): + """ + A table for identifying which objects in a file contain references to external resources. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["objects"] = Field( + "objects", + json_schema_extra={ + "linkml_meta": {"equals_string": "objects", "ifabsent": "string(objects)"} + }, + ) + files_idx: np.uint64 = Field( + ..., description="""The row index to the file in the `files` table containing the object.""" + ) + object_id: str = Field(..., description="""The object id (UUID) of the object.""") + object_type: str = Field(..., description="""The data type of the object.""") + relative_path: str = Field( + ..., + description="""The relative path from the data object with the `object_id` to the dataset or attribute with the value(s) that is associated with an external resource. This can be an empty string if the object is a dataset that contains the value(s) that is associated with an external resource.""", + ) + field: str = Field( + ..., + description="""The field within the compound data type using an external resource. This is used only if the dataset or attribute is a compound data type; otherwise this should be an empty string.""", + ) + + +class ExternalResourcesObjectKeys(Data): + """ + A table for identifying which objects use which keys. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["object_keys"] = Field( + "object_keys", + json_schema_extra={ + "linkml_meta": {"equals_string": "object_keys", "ifabsent": "string(object_keys)"} + }, + ) + objects_idx: np.uint64 = Field( + ..., description="""The row index to the object in the `objects` table that holds the key""" + ) + keys_idx: np.uint64 = Field( + ..., description="""The row index to the key in the `keys` table.""" + ) + + +class ExternalResourcesEntityKeys(Data): + """ + A table for identifying which keys use which entity. + """ + + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({"from_schema": "hdmf-experimental.resources"}) + + name: Literal["entity_keys"] = Field( + "entity_keys", + json_schema_extra={ + "linkml_meta": {"equals_string": "entity_keys", "ifabsent": "string(entity_keys)"} + }, + ) + entities_idx: np.uint64 = Field( + ..., description="""The row index to the entity in the `entities` table.""" + ) + keys_idx: np.uint64 = Field( + ..., description="""The row index to the key in the `keys` table.""" + ) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model +ExternalResources.model_rebuild() +ExternalResourcesKeys.model_rebuild() +ExternalResourcesFiles.model_rebuild() +ExternalResourcesEntities.model_rebuild() +ExternalResourcesObjects.model_rebuild() +ExternalResourcesObjectKeys.model_rebuild() +ExternalResourcesEntityKeys.model_rebuild() diff --git a/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/namespace.py b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/namespace.py new file mode 100644 index 0000000..c642308 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/models/pydantic/hdmf_experimental/v0_4_0/namespace.py @@ -0,0 +1,90 @@ +from __future__ import annotations +from datetime import datetime, date +from decimal import Decimal +from enum import Enum +import re +import sys +from typing import Any, ClassVar, List, Literal, Dict, Optional, Union +from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator +import numpy as np +from ...hdmf_experimental.v0_4_0.hdmf_experimental_resources import ( + ExternalResources, + ExternalResourcesKeys, + ExternalResourcesFiles, + ExternalResourcesEntities, + ExternalResourcesObjects, + ExternalResourcesObjectKeys, + ExternalResourcesEntityKeys, +) +from ...hdmf_common.v1_7_0.hdmf_common_sparse import CSRMatrix, CSRMatrixData +from ...hdmf_common.v1_7_0.hdmf_common_base import Data, Container, SimpleMultiContainer +from ...hdmf_common.v1_7_0.hdmf_common_table import ( + VectorData, + VectorIndex, + ElementIdentifiers, + DynamicTableRegion, + DynamicTable, + AlignedDynamicTable, +) +from ...hdmf_experimental.v0_4_0.hdmf_experimental_experimental import EnumData + +metamodel_version = "None" +version = "0.4.0" + + +class ConfiguredBaseModel(BaseModel): + model_config = ConfigDict( + validate_assignment=True, + validate_default=True, + extra="forbid", + arbitrary_types_allowed=True, + use_enum_values=True, + strict=False, + ) + hdf5_path: Optional[str] = Field( + None, description="The absolute path that this object is stored in an NWB file" + ) + object_id: Optional[str] = Field(None, description="Unique UUID for each object") + + +class LinkMLMeta(RootModel): + root: Dict[str, Any] = {} + model_config = ConfigDict(frozen=True) + + def __getattr__(self, key: str): + return getattr(self.root, key) + + def __getitem__(self, key: str): + return self.root[key] + + def __setitem__(self, key: str, value): + self.root[key] = value + + def __contains__(self, key: str) -> bool: + return key in self.root + + +linkml_meta = LinkMLMeta( + { + "annotations": { + "is_namespace": {"tag": "is_namespace", "value": True}, + "namespace": {"tag": "namespace", "value": "hdmf-experimental"}, + }, + "default_prefix": "hdmf-experimental/", + "description": ( + "Experimental data structures provided by HDMF. These are not " + "guaranteed to be available in the future." + ), + "id": "hdmf-experimental", + "imports": [ + "hdmf-experimental.experimental", + "hdmf-experimental.resources", + "hdmf-experimental.nwb.language", + ], + "name": "hdmf-experimental", + } +) + + +# Model rebuild +# see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_1_0/hdmf-common.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_1_0/hdmf-common.nwb.language.yaml index 68f0304..35776f9 100644 --- a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_1_0/hdmf-common.nwb.language.yaml +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_1_0/hdmf-common.nwb.language.yaml @@ -5,7 +5,7 @@ annotations: value: 'False' namespace: tag: namespace - value: core + value: hdmf-common description: Adapter objects to mimic the behavior of elements in the nwb-schema-language id: nwb.language imports: diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_1_2/hdmf-common.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_1_2/hdmf-common.nwb.language.yaml index 68f0304..35776f9 100644 --- a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_1_2/hdmf-common.nwb.language.yaml +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_1_2/hdmf-common.nwb.language.yaml @@ -5,7 +5,7 @@ annotations: value: 'False' namespace: tag: namespace - value: core + value: hdmf-common description: Adapter objects to mimic the behavior of elements in the nwb-schema-language id: nwb.language imports: diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_1_3/hdmf-common.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_1_3/hdmf-common.nwb.language.yaml index 68f0304..35776f9 100644 --- a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_1_3/hdmf-common.nwb.language.yaml +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_1_3/hdmf-common.nwb.language.yaml @@ -5,7 +5,7 @@ annotations: value: 'False' namespace: tag: namespace - value: core + value: hdmf-common description: Adapter objects to mimic the behavior of elements in the nwb-schema-language id: nwb.language imports: diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.base.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.base.yaml new file mode 100644 index 0000000..ff30beb --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.base.yaml @@ -0,0 +1,33 @@ +name: hdmf-common.base +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.base +version: 1.2.0 +imports: +- hdmf-common.nwb.language +default_prefix: hdmf-common.base/ +classes: + Data: + name: Data + description: An abstract data type for a dataset. + attributes: + name: + name: name + range: string + required: true + tree_root: true + Container: + name: Container + description: An abstract data type for a group storing collections of data and + metadata. Base type for all data and metadata containers. + attributes: + name: + name: name + range: string + required: true + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.nwb.language.yaml new file mode 100644 index 0000000..35776f9 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.nwb.language.yaml @@ -0,0 +1,109 @@ +name: hdmf-common.nwb.language +annotations: + is_namespace: + tag: is_namespace + value: 'False' + namespace: + tag: namespace + value: hdmf-common +description: Adapter objects to mimic the behavior of elements in the nwb-schema-language +id: nwb.language +imports: +- linkml:types +prefixes: + linkml: + prefix_prefix: linkml + prefix_reference: https://w3id.org/linkml +default_prefix: nwb.language/ +types: + float32: + name: float32 + typeof: float + repr: np.float32 + float64: + name: float64 + typeof: double + repr: np.float64 + long: + name: long + typeof: integer + repr: np.longlong + int64: + name: int64 + typeof: integer + repr: np.int64 + int: + name: int + typeof: integer + int32: + name: int32 + typeof: integer + repr: np.int32 + int16: + name: int16 + typeof: integer + repr: np.int16 + short: + name: short + typeof: integer + repr: np.int16 + int8: + name: int8 + typeof: integer + repr: np.int8 + uint: + name: uint + typeof: integer + repr: np.uint64 + minimum_value: 0 + uint32: + name: uint32 + typeof: integer + repr: np.uint32 + minimum_value: 0 + uint16: + name: uint16 + typeof: integer + repr: np.uint16 + minimum_value: 0 + uint8: + name: uint8 + typeof: integer + repr: np.uint8 + minimum_value: 0 + uint64: + name: uint64 + typeof: integer + repr: np.uint64 + minimum_value: 0 + numeric: + name: numeric + typeof: float + repr: np.number + text: + name: text + typeof: string + utf: + name: utf + typeof: string + utf8: + name: utf8 + typeof: string + utf_8: + name: utf_8 + typeof: string + ascii: + name: ascii + typeof: string + bool: + name: bool + typeof: boolean + isodatetime: + name: isodatetime + typeof: datetime + repr: np.datetime64 +classes: + AnyType: + name: AnyType + description: Needed because some classes in hdmf-common are datasets without dtype + class_uri: linkml:Any diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.sparse.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.sparse.yaml new file mode 100644 index 0000000..2d9616c --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.sparse.yaml @@ -0,0 +1,75 @@ +name: hdmf-common.sparse +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.sparse +version: 1.2.0 +imports: +- hdmf-common.nwb.language +default_prefix: hdmf-common.sparse/ +classes: + CSRMatrix: + name: CSRMatrix + description: a compressed sparse row matrix + attributes: + name: + name: name + range: string + required: true + shape: + name: shape + description: the shape of this sparse matrix + range: int + indices: + name: indices + description: column indices + range: CSRMatrix__indices + required: true + multivalued: false + indptr: + name: indptr + description: index pointer + range: CSRMatrix__indptr + required: true + multivalued: false + data: + name: data + description: values in the matrix + range: CSRMatrix__data + required: true + multivalued: false + tree_root: true + CSRMatrix__indices: + name: CSRMatrix__indices + description: column indices + attributes: + name: + name: name + ifabsent: string(indices) + range: string + required: true + equals_string: indices + CSRMatrix__indptr: + name: CSRMatrix__indptr + description: index pointer + attributes: + name: + name: name + ifabsent: string(indptr) + range: string + required: true + equals_string: indptr + CSRMatrix__data: + name: CSRMatrix__data + description: values in the matrix + attributes: + name: + name: name + ifabsent: string(data) + range: string + required: true + equals_string: data diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.table.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.table.yaml new file mode 100644 index 0000000..accfb99 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/hdmf-common.table.yaml @@ -0,0 +1,181 @@ +name: hdmf-common.table +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.table +version: 1.2.0 +imports: +- hdmf-common.base +- hdmf-common.nwb.language +default_prefix: hdmf-common.table/ +classes: + VectorData: + name: VectorData + description: An n-dimensional dataset representing a column of a DynamicTable. + If used without an accompanying VectorIndex, first dimension is along the rows + of the DynamicTable and each step along the first dimension is a cell of the + larger table. VectorData can also be used to represent a ragged array if paired + with a VectorIndex. This allows for storing arrays of varying length in a single + cell of the DynamicTable by indexing into this VectorData. The first vector + is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], + and so on. + is_a: Data + attributes: + name: + name: name + range: string + required: true + description: + name: description + description: Description of what these vectors represent. + range: text + array: + name: array + range: AnyType + any_of: + - array: + dimensions: + - alias: dim0 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - alias: dim2 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - alias: dim2 + - alias: dim3 + tree_root: true + VectorIndex: + name: VectorIndex + description: Used with VectorData to encode a ragged array. An array of indices + into the first dimension of the target VectorData, and forming a map between + the rows of a DynamicTable and the indices of the VectorData. The name of the + VectorIndex is expected to be the name of the target VectorData object followed + by "_index". + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + target: + name: target + description: Reference to the target dataset that this index applies to. + range: VectorData + tree_root: true + ElementIdentifiers: + name: ElementIdentifiers + description: A list of unique identifiers for values within a dataset, e.g. rows + of a DynamicTable. + is_a: Data + attributes: + name: + name: name + ifabsent: string(element_id) + range: string + required: true + tree_root: true + DynamicTableRegion: + name: DynamicTableRegion + description: DynamicTableRegion provides a link from one table to an index or + region of another. The `table` attribute is a link to another `DynamicTable`, + indicating which table is referenced, and the data is int(s) indicating the + row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to + associate rows with repeated meta-data without data duplication. They can also + be used to create hierarchical relationships between multiple `DynamicTable`s. + `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create + ragged references, so a single cell of a `DynamicTable` can reference many rows + of another `DynamicTable`. + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + table: + name: table + description: Reference to the DynamicTable object that this region applies + to. + range: DynamicTable + description: + name: description + description: Description of what this table region points to. + range: text + tree_root: true + VocabData: + name: VocabData + description: Data that come from a controlled vocabulary of text values. A data + value of i corresponds to the i-th element in the 'vocabulary' array attribute. + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + vocabulary: + name: vocabulary + description: The available items in the controlled vocabulary. + range: text + tree_root: true + DynamicTable: + name: DynamicTable + description: A group containing multiple datasets that are aligned on the first + dimension (Currently, this requirement if left up to APIs to check and enforce). + These datasets represent different columns in the table. Apart from a column + that contains unique identifiers for each row, there are no other required datasets. + Users are free to add any number of custom VectorData objects (columns) here. + DynamicTable also supports ragged array columns, where each element can be of + a different size. To add a ragged array column, use a VectorIndex type to index + the corresponding VectorData type. See documentation for VectorData and VectorIndex + for more details. Unlike a compound data type, which is analogous to storing + an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. + This provides an alternative structure to choose from when optimizing storage + for anticipated access patterns. Additionally, this type provides a way of creating + a table without having to define a compound type up front. Although this convenience + may be attractive, users should think carefully about how data will be accessed. + DynamicTable is more appropriate for column-centric access, whereas a dataset + with a compound type would be more appropriate for row-centric access. Finally, + data size should also be taken into account. For small tables, performance loss + may be an acceptable trade-off for the flexibility of a DynamicTable. + is_a: Container + attributes: + name: + name: name + range: string + required: true + colnames: + name: colnames + description: The names of the columns in this table. This should be used to + specify an order to the columns. + range: text + description: + name: description + description: Description of what is in this dynamic table. + range: text + id: + name: id + description: Array of unique identifiers for the rows of this dynamic table. + array: + dimensions: + - alias: num_rows + range: int + required: true + multivalued: false + vector_data: + name: vector_data + description: Vector columns, including index columns, of this dynamic table. + range: VectorData + required: false + multivalued: true + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/namespace.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/namespace.yaml new file mode 100644 index 0000000..7befc87 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_0/namespace.yaml @@ -0,0 +1,17 @@ +name: hdmf-common +annotations: + is_namespace: + tag: is_namespace + value: true + namespace: + tag: namespace + value: hdmf-common +description: Common data structures provided by HDMF +id: hdmf-common +version: 1.2.0 +imports: +- hdmf-common.base +- hdmf-common.table +- hdmf-common.sparse +- hdmf-common.nwb.language +default_prefix: hdmf-common/ diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.base.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.base.yaml new file mode 100644 index 0000000..17f8013 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.base.yaml @@ -0,0 +1,46 @@ +name: hdmf-common.base +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.base +version: 1.2.1 +imports: +- hdmf-common.nwb.language +default_prefix: hdmf-common.base/ +classes: + Data: + name: Data + description: An abstract data type for a dataset. + attributes: + name: + name: name + range: string + required: true + tree_root: true + Container: + name: Container + description: An abstract data type for a group storing collections of data and + metadata. Base type for all data and metadata containers. + attributes: + name: + name: name + range: string + required: true + tree_root: true + SimpleMultiContainer: + name: SimpleMultiContainer + description: A simple Container for holding onto multiple containers + is_a: Container + attributes: + children: + name: children + multivalued: true + inlined: true + inlined_as_list: false + any_of: + - range: Container + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.nwb.language.yaml new file mode 100644 index 0000000..35776f9 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.nwb.language.yaml @@ -0,0 +1,109 @@ +name: hdmf-common.nwb.language +annotations: + is_namespace: + tag: is_namespace + value: 'False' + namespace: + tag: namespace + value: hdmf-common +description: Adapter objects to mimic the behavior of elements in the nwb-schema-language +id: nwb.language +imports: +- linkml:types +prefixes: + linkml: + prefix_prefix: linkml + prefix_reference: https://w3id.org/linkml +default_prefix: nwb.language/ +types: + float32: + name: float32 + typeof: float + repr: np.float32 + float64: + name: float64 + typeof: double + repr: np.float64 + long: + name: long + typeof: integer + repr: np.longlong + int64: + name: int64 + typeof: integer + repr: np.int64 + int: + name: int + typeof: integer + int32: + name: int32 + typeof: integer + repr: np.int32 + int16: + name: int16 + typeof: integer + repr: np.int16 + short: + name: short + typeof: integer + repr: np.int16 + int8: + name: int8 + typeof: integer + repr: np.int8 + uint: + name: uint + typeof: integer + repr: np.uint64 + minimum_value: 0 + uint32: + name: uint32 + typeof: integer + repr: np.uint32 + minimum_value: 0 + uint16: + name: uint16 + typeof: integer + repr: np.uint16 + minimum_value: 0 + uint8: + name: uint8 + typeof: integer + repr: np.uint8 + minimum_value: 0 + uint64: + name: uint64 + typeof: integer + repr: np.uint64 + minimum_value: 0 + numeric: + name: numeric + typeof: float + repr: np.number + text: + name: text + typeof: string + utf: + name: utf + typeof: string + utf8: + name: utf8 + typeof: string + utf_8: + name: utf_8 + typeof: string + ascii: + name: ascii + typeof: string + bool: + name: bool + typeof: boolean + isodatetime: + name: isodatetime + typeof: datetime + repr: np.datetime64 +classes: + AnyType: + name: AnyType + description: Needed because some classes in hdmf-common are datasets without dtype + class_uri: linkml:Any diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.sparse.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.sparse.yaml new file mode 100644 index 0000000..b480dbe --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.sparse.yaml @@ -0,0 +1,77 @@ +name: hdmf-common.sparse +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.sparse +version: 1.2.1 +imports: +- hdmf-common.base +- hdmf-common.nwb.language +default_prefix: hdmf-common.sparse/ +classes: + CSRMatrix: + name: CSRMatrix + description: a compressed sparse row matrix + is_a: Container + attributes: + name: + name: name + range: string + required: true + shape: + name: shape + description: the shape of this sparse matrix + range: int + indices: + name: indices + description: column indices + range: CSRMatrix__indices + required: true + multivalued: false + indptr: + name: indptr + description: index pointer + range: CSRMatrix__indptr + required: true + multivalued: false + data: + name: data + description: values in the matrix + range: CSRMatrix__data + required: true + multivalued: false + tree_root: true + CSRMatrix__indices: + name: CSRMatrix__indices + description: column indices + attributes: + name: + name: name + ifabsent: string(indices) + range: string + required: true + equals_string: indices + CSRMatrix__indptr: + name: CSRMatrix__indptr + description: index pointer + attributes: + name: + name: name + ifabsent: string(indptr) + range: string + required: true + equals_string: indptr + CSRMatrix__data: + name: CSRMatrix__data + description: values in the matrix + attributes: + name: + name: name + ifabsent: string(data) + range: string + required: true + equals_string: data diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.table.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.table.yaml new file mode 100644 index 0000000..4b3b3ef --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/hdmf-common.table.yaml @@ -0,0 +1,181 @@ +name: hdmf-common.table +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.table +version: 1.2.1 +imports: +- hdmf-common.base +- hdmf-common.nwb.language +default_prefix: hdmf-common.table/ +classes: + VectorData: + name: VectorData + description: An n-dimensional dataset representing a column of a DynamicTable. + If used without an accompanying VectorIndex, first dimension is along the rows + of the DynamicTable and each step along the first dimension is a cell of the + larger table. VectorData can also be used to represent a ragged array if paired + with a VectorIndex. This allows for storing arrays of varying length in a single + cell of the DynamicTable by indexing into this VectorData. The first vector + is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], + and so on. + is_a: Data + attributes: + name: + name: name + range: string + required: true + description: + name: description + description: Description of what these vectors represent. + range: text + array: + name: array + range: AnyType + any_of: + - array: + dimensions: + - alias: dim0 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - alias: dim2 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - alias: dim2 + - alias: dim3 + tree_root: true + VectorIndex: + name: VectorIndex + description: Used with VectorData to encode a ragged array. An array of indices + into the first dimension of the target VectorData, and forming a map between + the rows of a DynamicTable and the indices of the VectorData. The name of the + VectorIndex is expected to be the name of the target VectorData object followed + by "_index". + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + target: + name: target + description: Reference to the target dataset that this index applies to. + range: VectorData + tree_root: true + ElementIdentifiers: + name: ElementIdentifiers + description: A list of unique identifiers for values within a dataset, e.g. rows + of a DynamicTable. + is_a: Data + attributes: + name: + name: name + ifabsent: string(element_id) + range: string + required: true + tree_root: true + DynamicTableRegion: + name: DynamicTableRegion + description: DynamicTableRegion provides a link from one table to an index or + region of another. The `table` attribute is a link to another `DynamicTable`, + indicating which table is referenced, and the data is int(s) indicating the + row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to + associate rows with repeated meta-data without data duplication. They can also + be used to create hierarchical relationships between multiple `DynamicTable`s. + `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create + ragged references, so a single cell of a `DynamicTable` can reference many rows + of another `DynamicTable`. + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + table: + name: table + description: Reference to the DynamicTable object that this region applies + to. + range: DynamicTable + description: + name: description + description: Description of what this table region points to. + range: text + tree_root: true + VocabData: + name: VocabData + description: Data that come from a controlled vocabulary of text values. A data + value of i corresponds to the i-th element in the 'vocabulary' array attribute. + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + vocabulary: + name: vocabulary + description: The available items in the controlled vocabulary. + range: text + tree_root: true + DynamicTable: + name: DynamicTable + description: A group containing multiple datasets that are aligned on the first + dimension (Currently, this requirement if left up to APIs to check and enforce). + These datasets represent different columns in the table. Apart from a column + that contains unique identifiers for each row, there are no other required datasets. + Users are free to add any number of custom VectorData objects (columns) here. + DynamicTable also supports ragged array columns, where each element can be of + a different size. To add a ragged array column, use a VectorIndex type to index + the corresponding VectorData type. See documentation for VectorData and VectorIndex + for more details. Unlike a compound data type, which is analogous to storing + an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. + This provides an alternative structure to choose from when optimizing storage + for anticipated access patterns. Additionally, this type provides a way of creating + a table without having to define a compound type up front. Although this convenience + may be attractive, users should think carefully about how data will be accessed. + DynamicTable is more appropriate for column-centric access, whereas a dataset + with a compound type would be more appropriate for row-centric access. Finally, + data size should also be taken into account. For small tables, performance loss + may be an acceptable trade-off for the flexibility of a DynamicTable. + is_a: Container + attributes: + name: + name: name + range: string + required: true + colnames: + name: colnames + description: The names of the columns in this table. This should be used to + specify an order to the columns. + range: text + description: + name: description + description: Description of what is in this dynamic table. + range: text + id: + name: id + description: Array of unique identifiers for the rows of this dynamic table. + array: + dimensions: + - alias: num_rows + range: int + required: true + multivalued: false + vector_data: + name: vector_data + description: Vector columns, including index columns, of this dynamic table. + range: VectorData + required: false + multivalued: true + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/namespace.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/namespace.yaml new file mode 100644 index 0000000..e29bfb9 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_2_1/namespace.yaml @@ -0,0 +1,17 @@ +name: hdmf-common +annotations: + is_namespace: + tag: is_namespace + value: true + namespace: + tag: namespace + value: hdmf-common +description: Common data structures provided by HDMF +id: hdmf-common +version: 1.2.1 +imports: +- hdmf-common.base +- hdmf-common.table +- hdmf-common.sparse +- hdmf-common.nwb.language +default_prefix: hdmf-common/ diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.base.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.base.yaml new file mode 100644 index 0000000..22efa9c --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.base.yaml @@ -0,0 +1,46 @@ +name: hdmf-common.base +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.base +version: 1.3.0 +imports: +- hdmf-common.nwb.language +default_prefix: hdmf-common.base/ +classes: + Data: + name: Data + description: An abstract data type for a dataset. + attributes: + name: + name: name + range: string + required: true + tree_root: true + Container: + name: Container + description: An abstract data type for a group storing collections of data and + metadata. Base type for all data and metadata containers. + attributes: + name: + name: name + range: string + required: true + tree_root: true + SimpleMultiContainer: + name: SimpleMultiContainer + description: A simple Container for holding onto multiple containers. + is_a: Container + attributes: + children: + name: children + multivalued: true + inlined: true + inlined_as_list: false + any_of: + - range: Container + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.nwb.language.yaml new file mode 100644 index 0000000..35776f9 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.nwb.language.yaml @@ -0,0 +1,109 @@ +name: hdmf-common.nwb.language +annotations: + is_namespace: + tag: is_namespace + value: 'False' + namespace: + tag: namespace + value: hdmf-common +description: Adapter objects to mimic the behavior of elements in the nwb-schema-language +id: nwb.language +imports: +- linkml:types +prefixes: + linkml: + prefix_prefix: linkml + prefix_reference: https://w3id.org/linkml +default_prefix: nwb.language/ +types: + float32: + name: float32 + typeof: float + repr: np.float32 + float64: + name: float64 + typeof: double + repr: np.float64 + long: + name: long + typeof: integer + repr: np.longlong + int64: + name: int64 + typeof: integer + repr: np.int64 + int: + name: int + typeof: integer + int32: + name: int32 + typeof: integer + repr: np.int32 + int16: + name: int16 + typeof: integer + repr: np.int16 + short: + name: short + typeof: integer + repr: np.int16 + int8: + name: int8 + typeof: integer + repr: np.int8 + uint: + name: uint + typeof: integer + repr: np.uint64 + minimum_value: 0 + uint32: + name: uint32 + typeof: integer + repr: np.uint32 + minimum_value: 0 + uint16: + name: uint16 + typeof: integer + repr: np.uint16 + minimum_value: 0 + uint8: + name: uint8 + typeof: integer + repr: np.uint8 + minimum_value: 0 + uint64: + name: uint64 + typeof: integer + repr: np.uint64 + minimum_value: 0 + numeric: + name: numeric + typeof: float + repr: np.number + text: + name: text + typeof: string + utf: + name: utf + typeof: string + utf8: + name: utf8 + typeof: string + utf_8: + name: utf_8 + typeof: string + ascii: + name: ascii + typeof: string + bool: + name: bool + typeof: boolean + isodatetime: + name: isodatetime + typeof: datetime + repr: np.datetime64 +classes: + AnyType: + name: AnyType + description: Needed because some classes in hdmf-common are datasets without dtype + class_uri: linkml:Any diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.resources.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.resources.yaml new file mode 100644 index 0000000..918a6a5 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.resources.yaml @@ -0,0 +1,158 @@ +name: hdmf-common.resources +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.resources +version: 1.3.0 +imports: +- hdmf-common.base +- hdmf-common.nwb.language +default_prefix: hdmf-common.resources/ +classes: + ExternalResources: + name: ExternalResources + description: 'A set of four tables for tracking external resource references in + a file. NOTE: this data type is in beta testing and is subject to change in + a later version.' + is_a: Container + attributes: + name: + name: name + range: string + required: true + keys: + name: keys + description: A table for storing user terms that are used to refer to external + resources. + range: ExternalResources__keys + required: true + multivalued: false + resources: + name: resources + description: A table for mapping user terms (i.e., keys) to resource entities. + range: ExternalResources__resources + required: true + multivalued: false + objects: + name: objects + description: A table for identifying which objects in a file contain references + to external resources. + range: ExternalResources__objects + required: true + multivalued: false + object_keys: + name: object_keys + description: A table for identifying which objects use which keys. + range: ExternalResources__object_keys + required: true + multivalued: false + tree_root: true + ExternalResources__keys: + name: ExternalResources__keys + description: A table for storing user terms that are used to refer to external + resources. + is_a: Data + attributes: + name: + name: name + ifabsent: string(keys) + range: string + required: true + equals_string: keys + key_name: + name: key_name + description: The user term that maps to one or more resources in the 'resources' + table. + range: text + required: true + multivalued: false + ExternalResources__resources: + name: ExternalResources__resources + description: A table for mapping user terms (i.e., keys) to resource entities. + is_a: Data + attributes: + name: + name: name + ifabsent: string(resources) + range: string + required: true + equals_string: resources + keytable_idx: + name: keytable_idx + description: The index to the key in the 'keys' table. + range: uint + required: true + multivalued: false + resource_name: + name: resource_name + description: The name of the online resource (e.g., website, database) that + has the entity. + range: text + required: true + multivalued: false + resource_id: + name: resource_id + description: The unique identifier for the resource entity at the resource. + range: text + required: true + multivalued: false + uri: + name: uri + description: The URI for the resource entity this reference applies to. This + can be an empty string. + range: text + required: true + multivalued: false + ExternalResources__objects: + name: ExternalResources__objects + description: A table for identifying which objects in a file contain references + to external resources. + is_a: Data + attributes: + name: + name: name + ifabsent: string(objects) + range: string + required: true + equals_string: objects + object_id: + name: object_id + description: The UUID for the object. + range: text + required: true + multivalued: false + field: + name: field + description: The field of the object. This can be an empty string if the object + is a dataset and the field is the dataset values. + range: text + required: true + multivalued: false + ExternalResources__object_keys: + name: ExternalResources__object_keys + description: A table for identifying which objects use which keys. + is_a: Data + attributes: + name: + name: name + ifabsent: string(object_keys) + range: string + required: true + equals_string: object_keys + objecttable_idx: + name: objecttable_idx + description: The index to the 'objects' table for the object that holds the + key. + range: uint + required: true + multivalued: false + keytable_idx: + name: keytable_idx + description: The index to the 'keys' table for the key. + range: uint + required: true + multivalued: false diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.sparse.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.sparse.yaml new file mode 100644 index 0000000..264e15d --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.sparse.yaml @@ -0,0 +1,66 @@ +name: hdmf-common.sparse +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.sparse +version: 1.3.0 +imports: +- hdmf-common.base +- hdmf-common.nwb.language +default_prefix: hdmf-common.sparse/ +classes: + CSRMatrix: + name: CSRMatrix + description: A compressed sparse row matrix. Data are stored in the standard CSR + format, where column indices for row i are stored in indices[indptr[i]:indptr[i+1]] + and their corresponding values are stored in data[indptr[i]:indptr[i+1]]. + is_a: Container + attributes: + name: + name: name + range: string + required: true + shape: + name: shape + description: The shape (number of rows, number of columns) of this sparse + matrix. + range: uint + indices: + name: indices + description: The column indices. + array: + dimensions: + - alias: number_of_non_zero_values + range: uint + required: true + multivalued: false + indptr: + name: indptr + description: The row index pointer. + array: + dimensions: + - alias: number_of_rows_in_the_matrix_1 + range: uint + required: true + multivalued: false + data: + name: data + description: The non-zero values in the matrix. + range: CSRMatrix__data + required: true + multivalued: false + tree_root: true + CSRMatrix__data: + name: CSRMatrix__data + description: The non-zero values in the matrix. + attributes: + name: + name: name + ifabsent: string(data) + range: string + required: true + equals_string: data diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.table.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.table.yaml new file mode 100644 index 0000000..478c18d --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/hdmf-common.table.yaml @@ -0,0 +1,181 @@ +name: hdmf-common.table +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.table +version: 1.3.0 +imports: +- hdmf-common.base +- hdmf-common.nwb.language +default_prefix: hdmf-common.table/ +classes: + VectorData: + name: VectorData + description: An n-dimensional dataset representing a column of a DynamicTable. + If used without an accompanying VectorIndex, first dimension is along the rows + of the DynamicTable and each step along the first dimension is a cell of the + larger table. VectorData can also be used to represent a ragged array if paired + with a VectorIndex. This allows for storing arrays of varying length in a single + cell of the DynamicTable by indexing into this VectorData. The first vector + is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], + and so on. + is_a: Data + attributes: + name: + name: name + range: string + required: true + description: + name: description + description: Description of what these vectors represent. + range: text + array: + name: array + range: AnyType + any_of: + - array: + dimensions: + - alias: dim0 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - alias: dim2 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - alias: dim2 + - alias: dim3 + tree_root: true + VectorIndex: + name: VectorIndex + description: Used with VectorData to encode a ragged array. An array of indices + into the first dimension of the target VectorData, and forming a map between + the rows of a DynamicTable and the indices of the VectorData. The name of the + VectorIndex is expected to be the name of the target VectorData object followed + by "_index". + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + target: + name: target + description: Reference to the target dataset that this index applies to. + range: VectorData + tree_root: true + ElementIdentifiers: + name: ElementIdentifiers + description: A list of unique identifiers for values within a dataset, e.g. rows + of a DynamicTable. + is_a: Data + attributes: + name: + name: name + ifabsent: string(element_id) + range: string + required: true + tree_root: true + DynamicTableRegion: + name: DynamicTableRegion + description: DynamicTableRegion provides a link from one table to an index or + region of another. The `table` attribute is a link to another `DynamicTable`, + indicating which table is referenced, and the data is int(s) indicating the + row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to + associate rows with repeated meta-data without data duplication. They can also + be used to create hierarchical relationships between multiple `DynamicTable`s. + `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create + ragged references, so a single cell of a `DynamicTable` can reference many rows + of another `DynamicTable`. + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + table: + name: table + description: Reference to the DynamicTable object that this region applies + to. + range: DynamicTable + description: + name: description + description: Description of what this table region points to. + range: text + tree_root: true + VocabData: + name: VocabData + description: Data that come from a controlled vocabulary of text values. A data + value of i corresponds to the i-th element in the 'vocabulary' array attribute. + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + vocabulary: + name: vocabulary + description: The available items in the controlled vocabulary. + range: text + tree_root: true + DynamicTable: + name: DynamicTable + description: A group containing multiple datasets that are aligned on the first + dimension (Currently, this requirement if left up to APIs to check and enforce). + These datasets represent different columns in the table. Apart from a column + that contains unique identifiers for each row, there are no other required datasets. + Users are free to add any number of custom VectorData objects (columns) here. + DynamicTable also supports ragged array columns, where each element can be of + a different size. To add a ragged array column, use a VectorIndex type to index + the corresponding VectorData type. See documentation for VectorData and VectorIndex + for more details. Unlike a compound data type, which is analogous to storing + an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. + This provides an alternative structure to choose from when optimizing storage + for anticipated access patterns. Additionally, this type provides a way of creating + a table without having to define a compound type up front. Although this convenience + may be attractive, users should think carefully about how data will be accessed. + DynamicTable is more appropriate for column-centric access, whereas a dataset + with a compound type would be more appropriate for row-centric access. Finally, + data size should also be taken into account. For small tables, performance loss + may be an acceptable trade-off for the flexibility of a DynamicTable. + is_a: Container + attributes: + name: + name: name + range: string + required: true + colnames: + name: colnames + description: The names of the columns in this table. This should be used to + specify an order to the columns. + range: text + description: + name: description + description: Description of what is in this dynamic table. + range: text + id: + name: id + description: Array of unique identifiers for the rows of this dynamic table. + array: + dimensions: + - alias: num_rows + range: int + required: true + multivalued: false + vector_data: + name: vector_data + description: Vector columns, including index columns, of this dynamic table. + range: VectorData + required: false + multivalued: true + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/namespace.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/namespace.yaml new file mode 100644 index 0000000..11885e7 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_3_0/namespace.yaml @@ -0,0 +1,18 @@ +name: hdmf-common +annotations: + is_namespace: + tag: is_namespace + value: true + namespace: + tag: namespace + value: hdmf-common +description: Common data structures provided by HDMF +id: hdmf-common +version: 1.3.0 +imports: +- hdmf-common.base +- hdmf-common.table +- hdmf-common.sparse +- hdmf-common.resources +- hdmf-common.nwb.language +default_prefix: hdmf-common/ diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.base.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.base.yaml new file mode 100644 index 0000000..ef51ebd --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.base.yaml @@ -0,0 +1,46 @@ +name: hdmf-common.base +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.base +version: 1.4.0 +imports: +- hdmf-common.nwb.language +default_prefix: hdmf-common.base/ +classes: + Data: + name: Data + description: An abstract data type for a dataset. + attributes: + name: + name: name + range: string + required: true + tree_root: true + Container: + name: Container + description: An abstract data type for a group storing collections of data and + metadata. Base type for all data and metadata containers. + attributes: + name: + name: name + range: string + required: true + tree_root: true + SimpleMultiContainer: + name: SimpleMultiContainer + description: A simple Container for holding onto multiple containers. + is_a: Container + attributes: + children: + name: children + multivalued: true + inlined: true + inlined_as_list: false + any_of: + - range: Container + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.nwb.language.yaml new file mode 100644 index 0000000..98c00e4 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.nwb.language.yaml @@ -0,0 +1,109 @@ +name: hdmf-common.nwb.language +annotations: + is_namespace: + tag: is_namespace + value: 'False' + namespace: + tag: namespace + value: hdmf-experimental +description: Adapter objects to mimic the behavior of elements in the nwb-schema-language +id: nwb.language +imports: +- linkml:types +prefixes: + linkml: + prefix_prefix: linkml + prefix_reference: https://w3id.org/linkml +default_prefix: nwb.language/ +types: + float32: + name: float32 + typeof: float + repr: np.float32 + float64: + name: float64 + typeof: double + repr: np.float64 + long: + name: long + typeof: integer + repr: np.longlong + int64: + name: int64 + typeof: integer + repr: np.int64 + int: + name: int + typeof: integer + int32: + name: int32 + typeof: integer + repr: np.int32 + int16: + name: int16 + typeof: integer + repr: np.int16 + short: + name: short + typeof: integer + repr: np.int16 + int8: + name: int8 + typeof: integer + repr: np.int8 + uint: + name: uint + typeof: integer + repr: np.uint64 + minimum_value: 0 + uint32: + name: uint32 + typeof: integer + repr: np.uint32 + minimum_value: 0 + uint16: + name: uint16 + typeof: integer + repr: np.uint16 + minimum_value: 0 + uint8: + name: uint8 + typeof: integer + repr: np.uint8 + minimum_value: 0 + uint64: + name: uint64 + typeof: integer + repr: np.uint64 + minimum_value: 0 + numeric: + name: numeric + typeof: float + repr: np.number + text: + name: text + typeof: string + utf: + name: utf + typeof: string + utf8: + name: utf8 + typeof: string + utf_8: + name: utf_8 + typeof: string + ascii: + name: ascii + typeof: string + bool: + name: bool + typeof: boolean + isodatetime: + name: isodatetime + typeof: datetime + repr: np.datetime64 +classes: + AnyType: + name: AnyType + description: Needed because some classes in hdmf-common are datasets without dtype + class_uri: linkml:Any diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.sparse.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.sparse.yaml new file mode 100644 index 0000000..a004e10 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.sparse.yaml @@ -0,0 +1,66 @@ +name: hdmf-common.sparse +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.sparse +version: 1.4.0 +imports: +- hdmf-common.base +- hdmf-common.nwb.language +default_prefix: hdmf-common.sparse/ +classes: + CSRMatrix: + name: CSRMatrix + description: A compressed sparse row matrix. Data are stored in the standard CSR + format, where column indices for row i are stored in indices[indptr[i]:indptr[i+1]] + and their corresponding values are stored in data[indptr[i]:indptr[i+1]]. + is_a: Container + attributes: + name: + name: name + range: string + required: true + shape: + name: shape + description: The shape (number of rows, number of columns) of this sparse + matrix. + range: uint + indices: + name: indices + description: The column indices. + array: + dimensions: + - alias: number_of_non_zero_values + range: uint + required: true + multivalued: false + indptr: + name: indptr + description: The row index pointer. + array: + dimensions: + - alias: number_of_rows_in_the_matrix_1 + range: uint + required: true + multivalued: false + data: + name: data + description: The non-zero values in the matrix. + range: CSRMatrix__data + required: true + multivalued: false + tree_root: true + CSRMatrix__data: + name: CSRMatrix__data + description: The non-zero values in the matrix. + attributes: + name: + name: name + ifabsent: string(data) + range: string + required: true + equals_string: data diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.table.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.table.yaml new file mode 100644 index 0000000..ca9ead2 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/hdmf-common.table.yaml @@ -0,0 +1,166 @@ +name: hdmf-common.table +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.table +version: 1.4.0 +imports: +- hdmf-common.base +- hdmf-common.nwb.language +default_prefix: hdmf-common.table/ +classes: + VectorData: + name: VectorData + description: An n-dimensional dataset representing a column of a DynamicTable. + If used without an accompanying VectorIndex, first dimension is along the rows + of the DynamicTable and each step along the first dimension is a cell of the + larger table. VectorData can also be used to represent a ragged array if paired + with a VectorIndex. This allows for storing arrays of varying length in a single + cell of the DynamicTable by indexing into this VectorData. The first vector + is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], + and so on. + is_a: Data + attributes: + name: + name: name + range: string + required: true + description: + name: description + description: Description of what these vectors represent. + range: text + array: + name: array + range: AnyType + any_of: + - array: + dimensions: + - alias: dim0 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - alias: dim2 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - alias: dim2 + - alias: dim3 + tree_root: true + VectorIndex: + name: VectorIndex + description: Used with VectorData to encode a ragged array. An array of indices + into the first dimension of the target VectorData, and forming a map between + the rows of a DynamicTable and the indices of the VectorData. The name of the + VectorIndex is expected to be the name of the target VectorData object followed + by "_index". + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + target: + name: target + description: Reference to the target dataset that this index applies to. + range: VectorData + tree_root: true + ElementIdentifiers: + name: ElementIdentifiers + description: A list of unique identifiers for values within a dataset, e.g. rows + of a DynamicTable. + is_a: Data + attributes: + name: + name: name + ifabsent: string(element_id) + range: string + required: true + tree_root: true + DynamicTableRegion: + name: DynamicTableRegion + description: DynamicTableRegion provides a link from one table to an index or + region of another. The `table` attribute is a link to another `DynamicTable`, + indicating which table is referenced, and the data is int(s) indicating the + row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to + associate rows with repeated meta-data without data duplication. They can also + be used to create hierarchical relationships between multiple `DynamicTable`s. + `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create + ragged references, so a single cell of a `DynamicTable` can reference many rows + of another `DynamicTable`. + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + table: + name: table + description: Reference to the DynamicTable object that this region applies + to. + range: DynamicTable + description: + name: description + description: Description of what this table region points to. + range: text + tree_root: true + DynamicTable: + name: DynamicTable + description: A group containing multiple datasets that are aligned on the first + dimension (Currently, this requirement if left up to APIs to check and enforce). + These datasets represent different columns in the table. Apart from a column + that contains unique identifiers for each row, there are no other required datasets. + Users are free to add any number of custom VectorData objects (columns) here. + DynamicTable also supports ragged array columns, where each element can be of + a different size. To add a ragged array column, use a VectorIndex type to index + the corresponding VectorData type. See documentation for VectorData and VectorIndex + for more details. Unlike a compound data type, which is analogous to storing + an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. + This provides an alternative structure to choose from when optimizing storage + for anticipated access patterns. Additionally, this type provides a way of creating + a table without having to define a compound type up front. Although this convenience + may be attractive, users should think carefully about how data will be accessed. + DynamicTable is more appropriate for column-centric access, whereas a dataset + with a compound type would be more appropriate for row-centric access. Finally, + data size should also be taken into account. For small tables, performance loss + may be an acceptable trade-off for the flexibility of a DynamicTable. + is_a: Container + attributes: + name: + name: name + range: string + required: true + colnames: + name: colnames + description: The names of the columns in this table. This should be used to + specify an order to the columns. + range: text + description: + name: description + description: Description of what is in this dynamic table. + range: text + id: + name: id + description: Array of unique identifiers for the rows of this dynamic table. + array: + dimensions: + - alias: num_rows + range: int + required: true + multivalued: false + vector_data: + name: vector_data + description: Vector columns, including index columns, of this dynamic table. + range: VectorData + required: false + multivalued: true + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/namespace.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/namespace.yaml new file mode 100644 index 0000000..50680da --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_4_0/namespace.yaml @@ -0,0 +1,17 @@ +name: hdmf-common +annotations: + is_namespace: + tag: is_namespace + value: true + namespace: + tag: namespace + value: hdmf-common +description: Common data structures provided by HDMF +id: hdmf-common +version: 1.4.0 +imports: +- hdmf-common.base +- hdmf-common.table +- hdmf-common.sparse +- hdmf-common.nwb.language +default_prefix: hdmf-common/ diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_0/hdmf-common.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_0/hdmf-common.nwb.language.yaml index 68f0304..98c00e4 100644 --- a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_0/hdmf-common.nwb.language.yaml +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_0/hdmf-common.nwb.language.yaml @@ -5,7 +5,7 @@ annotations: value: 'False' namespace: tag: namespace - value: core + value: hdmf-experimental description: Adapter objects to mimic the behavior of elements in the nwb-schema-language id: nwb.language imports: diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.base.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.base.yaml new file mode 100644 index 0000000..75e5a6c --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.base.yaml @@ -0,0 +1,46 @@ +name: hdmf-common.base +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.base +version: 1.5.1 +imports: +- hdmf-common.nwb.language +default_prefix: hdmf-common.base/ +classes: + Data: + name: Data + description: An abstract data type for a dataset. + attributes: + name: + name: name + range: string + required: true + tree_root: true + Container: + name: Container + description: An abstract data type for a group storing collections of data and + metadata. Base type for all data and metadata containers. + attributes: + name: + name: name + range: string + required: true + tree_root: true + SimpleMultiContainer: + name: SimpleMultiContainer + description: A simple Container for holding onto multiple containers. + is_a: Container + attributes: + children: + name: children + multivalued: true + inlined: true + inlined_as_list: false + any_of: + - range: Container + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.nwb.language.yaml new file mode 100644 index 0000000..98c00e4 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.nwb.language.yaml @@ -0,0 +1,109 @@ +name: hdmf-common.nwb.language +annotations: + is_namespace: + tag: is_namespace + value: 'False' + namespace: + tag: namespace + value: hdmf-experimental +description: Adapter objects to mimic the behavior of elements in the nwb-schema-language +id: nwb.language +imports: +- linkml:types +prefixes: + linkml: + prefix_prefix: linkml + prefix_reference: https://w3id.org/linkml +default_prefix: nwb.language/ +types: + float32: + name: float32 + typeof: float + repr: np.float32 + float64: + name: float64 + typeof: double + repr: np.float64 + long: + name: long + typeof: integer + repr: np.longlong + int64: + name: int64 + typeof: integer + repr: np.int64 + int: + name: int + typeof: integer + int32: + name: int32 + typeof: integer + repr: np.int32 + int16: + name: int16 + typeof: integer + repr: np.int16 + short: + name: short + typeof: integer + repr: np.int16 + int8: + name: int8 + typeof: integer + repr: np.int8 + uint: + name: uint + typeof: integer + repr: np.uint64 + minimum_value: 0 + uint32: + name: uint32 + typeof: integer + repr: np.uint32 + minimum_value: 0 + uint16: + name: uint16 + typeof: integer + repr: np.uint16 + minimum_value: 0 + uint8: + name: uint8 + typeof: integer + repr: np.uint8 + minimum_value: 0 + uint64: + name: uint64 + typeof: integer + repr: np.uint64 + minimum_value: 0 + numeric: + name: numeric + typeof: float + repr: np.number + text: + name: text + typeof: string + utf: + name: utf + typeof: string + utf8: + name: utf8 + typeof: string + utf_8: + name: utf_8 + typeof: string + ascii: + name: ascii + typeof: string + bool: + name: bool + typeof: boolean + isodatetime: + name: isodatetime + typeof: datetime + repr: np.datetime64 +classes: + AnyType: + name: AnyType + description: Needed because some classes in hdmf-common are datasets without dtype + class_uri: linkml:Any diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.sparse.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.sparse.yaml new file mode 100644 index 0000000..380bc7e --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.sparse.yaml @@ -0,0 +1,66 @@ +name: hdmf-common.sparse +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.sparse +version: 1.5.1 +imports: +- hdmf-common.base +- hdmf-common.nwb.language +default_prefix: hdmf-common.sparse/ +classes: + CSRMatrix: + name: CSRMatrix + description: A compressed sparse row matrix. Data are stored in the standard CSR + format, where column indices for row i are stored in indices[indptr[i]:indptr[i+1]] + and their corresponding values are stored in data[indptr[i]:indptr[i+1]]. + is_a: Container + attributes: + name: + name: name + range: string + required: true + shape: + name: shape + description: The shape (number of rows, number of columns) of this sparse + matrix. + range: uint + indices: + name: indices + description: The column indices. + array: + dimensions: + - alias: number_of_non_zero_values + range: uint + required: true + multivalued: false + indptr: + name: indptr + description: The row index pointer. + array: + dimensions: + - alias: number_of_rows_in_the_matrix_1 + range: uint + required: true + multivalued: false + data: + name: data + description: The non-zero values in the matrix. + range: CSRMatrix__data + required: true + multivalued: false + tree_root: true + CSRMatrix__data: + name: CSRMatrix__data + description: The non-zero values in the matrix. + attributes: + name: + name: name + ifabsent: string(data) + range: string + required: true + equals_string: data diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.table.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.table.yaml new file mode 100644 index 0000000..557721d --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/hdmf-common.table.yaml @@ -0,0 +1,185 @@ +name: hdmf-common.table +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.table +version: 1.5.1 +imports: +- hdmf-common.base +- hdmf-common.nwb.language +default_prefix: hdmf-common.table/ +classes: + VectorData: + name: VectorData + description: An n-dimensional dataset representing a column of a DynamicTable. + If used without an accompanying VectorIndex, first dimension is along the rows + of the DynamicTable and each step along the first dimension is a cell of the + larger table. VectorData can also be used to represent a ragged array if paired + with a VectorIndex. This allows for storing arrays of varying length in a single + cell of the DynamicTable by indexing into this VectorData. The first vector + is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], + and so on. + is_a: Data + attributes: + name: + name: name + range: string + required: true + description: + name: description + description: Description of what these vectors represent. + range: text + array: + name: array + range: AnyType + any_of: + - array: + dimensions: + - alias: dim0 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - alias: dim2 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - alias: dim2 + - alias: dim3 + tree_root: true + VectorIndex: + name: VectorIndex + description: Used with VectorData to encode a ragged array. An array of indices + into the first dimension of the target VectorData, and forming a map between + the rows of a DynamicTable and the indices of the VectorData. The name of the + VectorIndex is expected to be the name of the target VectorData object followed + by "_index". + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + target: + name: target + description: Reference to the target dataset that this index applies to. + range: VectorData + tree_root: true + ElementIdentifiers: + name: ElementIdentifiers + description: A list of unique identifiers for values within a dataset, e.g. rows + of a DynamicTable. + is_a: Data + attributes: + name: + name: name + ifabsent: string(element_id) + range: string + required: true + tree_root: true + DynamicTableRegion: + name: DynamicTableRegion + description: DynamicTableRegion provides a link from one table to an index or + region of another. The `table` attribute is a link to another `DynamicTable`, + indicating which table is referenced, and the data is int(s) indicating the + row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to + associate rows with repeated meta-data without data duplication. They can also + be used to create hierarchical relationships between multiple `DynamicTable`s. + `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create + ragged references, so a single cell of a `DynamicTable` can reference many rows + of another `DynamicTable`. + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + table: + name: table + description: Reference to the DynamicTable object that this region applies + to. + range: DynamicTable + description: + name: description + description: Description of what this table region points to. + range: text + tree_root: true + DynamicTable: + name: DynamicTable + description: A group containing multiple datasets that are aligned on the first + dimension (Currently, this requirement if left up to APIs to check and enforce). + These datasets represent different columns in the table. Apart from a column + that contains unique identifiers for each row, there are no other required datasets. + Users are free to add any number of custom VectorData objects (columns) here. + DynamicTable also supports ragged array columns, where each element can be of + a different size. To add a ragged array column, use a VectorIndex type to index + the corresponding VectorData type. See documentation for VectorData and VectorIndex + for more details. Unlike a compound data type, which is analogous to storing + an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. + This provides an alternative structure to choose from when optimizing storage + for anticipated access patterns. Additionally, this type provides a way of creating + a table without having to define a compound type up front. Although this convenience + may be attractive, users should think carefully about how data will be accessed. + DynamicTable is more appropriate for column-centric access, whereas a dataset + with a compound type would be more appropriate for row-centric access. Finally, + data size should also be taken into account. For small tables, performance loss + may be an acceptable trade-off for the flexibility of a DynamicTable. + is_a: Container + attributes: + name: + name: name + range: string + required: true + colnames: + name: colnames + description: The names of the columns in this table. This should be used to + specify an order to the columns. + range: text + description: + name: description + description: Description of what is in this dynamic table. + range: text + id: + name: id + description: Array of unique identifiers for the rows of this dynamic table. + array: + dimensions: + - alias: num_rows + range: int + required: true + multivalued: false + vector_data: + name: vector_data + description: Vector columns, including index columns, of this dynamic table. + range: VectorData + required: false + multivalued: true + tree_root: true + AlignedDynamicTable: + name: AlignedDynamicTable + description: DynamicTable container that supports storing a collection of sub-tables. + Each sub-table is a DynamicTable itself that is aligned with the main table + by row index. I.e., all DynamicTables stored in this group MUST have the same + number of rows. This type effectively defines a 2-level table in which the main + data is stored in the main table implemented by this type and additional columns + of the table are grouped into categories, with each category being represented + by a separate DynamicTable stored within the group. + is_a: DynamicTable + attributes: + children: + name: children + multivalued: true + inlined: true + inlined_as_list: false + any_of: + - range: DynamicTable + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/namespace.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/namespace.yaml new file mode 100644 index 0000000..917870d --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_5_1/namespace.yaml @@ -0,0 +1,17 @@ +name: hdmf-common +annotations: + is_namespace: + tag: is_namespace + value: true + namespace: + tag: namespace + value: hdmf-common +description: Common data structures provided by HDMF +id: hdmf-common +version: 1.5.1 +imports: +- hdmf-common.base +- hdmf-common.table +- hdmf-common.sparse +- hdmf-common.nwb.language +default_prefix: hdmf-common/ diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.base.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.base.yaml new file mode 100644 index 0000000..7031d84 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.base.yaml @@ -0,0 +1,46 @@ +name: hdmf-common.base +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.base +version: 1.6.0 +imports: +- hdmf-common.nwb.language +default_prefix: hdmf-common.base/ +classes: + Data: + name: Data + description: An abstract data type for a dataset. + attributes: + name: + name: name + range: string + required: true + tree_root: true + Container: + name: Container + description: An abstract data type for a group storing collections of data and + metadata. Base type for all data and metadata containers. + attributes: + name: + name: name + range: string + required: true + tree_root: true + SimpleMultiContainer: + name: SimpleMultiContainer + description: A simple Container for holding onto multiple containers. + is_a: Container + attributes: + children: + name: children + multivalued: true + inlined: true + inlined_as_list: false + any_of: + - range: Container + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.nwb.language.yaml new file mode 100644 index 0000000..98c00e4 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.nwb.language.yaml @@ -0,0 +1,109 @@ +name: hdmf-common.nwb.language +annotations: + is_namespace: + tag: is_namespace + value: 'False' + namespace: + tag: namespace + value: hdmf-experimental +description: Adapter objects to mimic the behavior of elements in the nwb-schema-language +id: nwb.language +imports: +- linkml:types +prefixes: + linkml: + prefix_prefix: linkml + prefix_reference: https://w3id.org/linkml +default_prefix: nwb.language/ +types: + float32: + name: float32 + typeof: float + repr: np.float32 + float64: + name: float64 + typeof: double + repr: np.float64 + long: + name: long + typeof: integer + repr: np.longlong + int64: + name: int64 + typeof: integer + repr: np.int64 + int: + name: int + typeof: integer + int32: + name: int32 + typeof: integer + repr: np.int32 + int16: + name: int16 + typeof: integer + repr: np.int16 + short: + name: short + typeof: integer + repr: np.int16 + int8: + name: int8 + typeof: integer + repr: np.int8 + uint: + name: uint + typeof: integer + repr: np.uint64 + minimum_value: 0 + uint32: + name: uint32 + typeof: integer + repr: np.uint32 + minimum_value: 0 + uint16: + name: uint16 + typeof: integer + repr: np.uint16 + minimum_value: 0 + uint8: + name: uint8 + typeof: integer + repr: np.uint8 + minimum_value: 0 + uint64: + name: uint64 + typeof: integer + repr: np.uint64 + minimum_value: 0 + numeric: + name: numeric + typeof: float + repr: np.number + text: + name: text + typeof: string + utf: + name: utf + typeof: string + utf8: + name: utf8 + typeof: string + utf_8: + name: utf_8 + typeof: string + ascii: + name: ascii + typeof: string + bool: + name: bool + typeof: boolean + isodatetime: + name: isodatetime + typeof: datetime + repr: np.datetime64 +classes: + AnyType: + name: AnyType + description: Needed because some classes in hdmf-common are datasets without dtype + class_uri: linkml:Any diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.sparse.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.sparse.yaml new file mode 100644 index 0000000..e2e8cff --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.sparse.yaml @@ -0,0 +1,66 @@ +name: hdmf-common.sparse +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.sparse +version: 1.6.0 +imports: +- hdmf-common.base +- hdmf-common.nwb.language +default_prefix: hdmf-common.sparse/ +classes: + CSRMatrix: + name: CSRMatrix + description: A compressed sparse row matrix. Data are stored in the standard CSR + format, where column indices for row i are stored in indices[indptr[i]:indptr[i+1]] + and their corresponding values are stored in data[indptr[i]:indptr[i+1]]. + is_a: Container + attributes: + name: + name: name + range: string + required: true + shape: + name: shape + description: The shape (number of rows, number of columns) of this sparse + matrix. + range: uint + indices: + name: indices + description: The column indices. + array: + dimensions: + - alias: number_of_non_zero_values + range: uint + required: true + multivalued: false + indptr: + name: indptr + description: The row index pointer. + array: + dimensions: + - alias: number_of_rows_in_the_matrix_1 + range: uint + required: true + multivalued: false + data: + name: data + description: The non-zero values in the matrix. + range: CSRMatrix__data + required: true + multivalued: false + tree_root: true + CSRMatrix__data: + name: CSRMatrix__data + description: The non-zero values in the matrix. + attributes: + name: + name: name + ifabsent: string(data) + range: string + required: true + equals_string: data diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.table.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.table.yaml new file mode 100644 index 0000000..100ea47 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/hdmf-common.table.yaml @@ -0,0 +1,185 @@ +name: hdmf-common.table +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.table +version: 1.6.0 +imports: +- hdmf-common.base +- hdmf-common.nwb.language +default_prefix: hdmf-common.table/ +classes: + VectorData: + name: VectorData + description: An n-dimensional dataset representing a column of a DynamicTable. + If used without an accompanying VectorIndex, first dimension is along the rows + of the DynamicTable and each step along the first dimension is a cell of the + larger table. VectorData can also be used to represent a ragged array if paired + with a VectorIndex. This allows for storing arrays of varying length in a single + cell of the DynamicTable by indexing into this VectorData. The first vector + is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], + and so on. + is_a: Data + attributes: + name: + name: name + range: string + required: true + description: + name: description + description: Description of what these vectors represent. + range: text + array: + name: array + range: AnyType + any_of: + - array: + dimensions: + - alias: dim0 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - alias: dim2 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - alias: dim2 + - alias: dim3 + tree_root: true + VectorIndex: + name: VectorIndex + description: Used with VectorData to encode a ragged array. An array of indices + into the first dimension of the target VectorData, and forming a map between + the rows of a DynamicTable and the indices of the VectorData. The name of the + VectorIndex is expected to be the name of the target VectorData object followed + by "_index". + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + target: + name: target + description: Reference to the target dataset that this index applies to. + range: VectorData + tree_root: true + ElementIdentifiers: + name: ElementIdentifiers + description: A list of unique identifiers for values within a dataset, e.g. rows + of a DynamicTable. + is_a: Data + attributes: + name: + name: name + ifabsent: string(element_id) + range: string + required: true + tree_root: true + DynamicTableRegion: + name: DynamicTableRegion + description: DynamicTableRegion provides a link from one table to an index or + region of another. The `table` attribute is a link to another `DynamicTable`, + indicating which table is referenced, and the data is int(s) indicating the + row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to + associate rows with repeated meta-data without data duplication. They can also + be used to create hierarchical relationships between multiple `DynamicTable`s. + `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create + ragged references, so a single cell of a `DynamicTable` can reference many rows + of another `DynamicTable`. + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + table: + name: table + description: Reference to the DynamicTable object that this region applies + to. + range: DynamicTable + description: + name: description + description: Description of what this table region points to. + range: text + tree_root: true + DynamicTable: + name: DynamicTable + description: A group containing multiple datasets that are aligned on the first + dimension (Currently, this requirement if left up to APIs to check and enforce). + These datasets represent different columns in the table. Apart from a column + that contains unique identifiers for each row, there are no other required datasets. + Users are free to add any number of custom VectorData objects (columns) here. + DynamicTable also supports ragged array columns, where each element can be of + a different size. To add a ragged array column, use a VectorIndex type to index + the corresponding VectorData type. See documentation for VectorData and VectorIndex + for more details. Unlike a compound data type, which is analogous to storing + an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. + This provides an alternative structure to choose from when optimizing storage + for anticipated access patterns. Additionally, this type provides a way of creating + a table without having to define a compound type up front. Although this convenience + may be attractive, users should think carefully about how data will be accessed. + DynamicTable is more appropriate for column-centric access, whereas a dataset + with a compound type would be more appropriate for row-centric access. Finally, + data size should also be taken into account. For small tables, performance loss + may be an acceptable trade-off for the flexibility of a DynamicTable. + is_a: Container + attributes: + name: + name: name + range: string + required: true + colnames: + name: colnames + description: The names of the columns in this table. This should be used to + specify an order to the columns. + range: text + description: + name: description + description: Description of what is in this dynamic table. + range: text + id: + name: id + description: Array of unique identifiers for the rows of this dynamic table. + array: + dimensions: + - alias: num_rows + range: int + required: true + multivalued: false + vector_data: + name: vector_data + description: Vector columns, including index columns, of this dynamic table. + range: VectorData + required: false + multivalued: true + tree_root: true + AlignedDynamicTable: + name: AlignedDynamicTable + description: DynamicTable container that supports storing a collection of sub-tables. + Each sub-table is a DynamicTable itself that is aligned with the main table + by row index. I.e., all DynamicTables stored in this group MUST have the same + number of rows. This type effectively defines a 2-level table in which the main + data is stored in the main table implemented by this type and additional columns + of the table are grouped into categories, with each category being represented + by a separate DynamicTable stored within the group. + is_a: DynamicTable + attributes: + children: + name: children + multivalued: true + inlined: true + inlined_as_list: false + any_of: + - range: DynamicTable + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/namespace.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/namespace.yaml new file mode 100644 index 0000000..241b849 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_6_0/namespace.yaml @@ -0,0 +1,17 @@ +name: hdmf-common +annotations: + is_namespace: + tag: is_namespace + value: true + namespace: + tag: namespace + value: hdmf-common +description: Common data structures provided by HDMF +id: hdmf-common +version: 1.6.0 +imports: +- hdmf-common.base +- hdmf-common.table +- hdmf-common.sparse +- hdmf-common.nwb.language +default_prefix: hdmf-common/ diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.base.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.base.yaml new file mode 100644 index 0000000..acf8a08 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.base.yaml @@ -0,0 +1,46 @@ +name: hdmf-common.base +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.base +version: 1.7.0 +imports: +- hdmf-common.nwb.language +default_prefix: hdmf-common.base/ +classes: + Data: + name: Data + description: An abstract data type for a dataset. + attributes: + name: + name: name + range: string + required: true + tree_root: true + Container: + name: Container + description: An abstract data type for a group storing collections of data and + metadata. Base type for all data and metadata containers. + attributes: + name: + name: name + range: string + required: true + tree_root: true + SimpleMultiContainer: + name: SimpleMultiContainer + description: A simple Container for holding onto multiple containers. + is_a: Container + attributes: + children: + name: children + multivalued: true + inlined: true + inlined_as_list: false + any_of: + - range: Container + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.nwb.language.yaml new file mode 100644 index 0000000..98c00e4 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.nwb.language.yaml @@ -0,0 +1,109 @@ +name: hdmf-common.nwb.language +annotations: + is_namespace: + tag: is_namespace + value: 'False' + namespace: + tag: namespace + value: hdmf-experimental +description: Adapter objects to mimic the behavior of elements in the nwb-schema-language +id: nwb.language +imports: +- linkml:types +prefixes: + linkml: + prefix_prefix: linkml + prefix_reference: https://w3id.org/linkml +default_prefix: nwb.language/ +types: + float32: + name: float32 + typeof: float + repr: np.float32 + float64: + name: float64 + typeof: double + repr: np.float64 + long: + name: long + typeof: integer + repr: np.longlong + int64: + name: int64 + typeof: integer + repr: np.int64 + int: + name: int + typeof: integer + int32: + name: int32 + typeof: integer + repr: np.int32 + int16: + name: int16 + typeof: integer + repr: np.int16 + short: + name: short + typeof: integer + repr: np.int16 + int8: + name: int8 + typeof: integer + repr: np.int8 + uint: + name: uint + typeof: integer + repr: np.uint64 + minimum_value: 0 + uint32: + name: uint32 + typeof: integer + repr: np.uint32 + minimum_value: 0 + uint16: + name: uint16 + typeof: integer + repr: np.uint16 + minimum_value: 0 + uint8: + name: uint8 + typeof: integer + repr: np.uint8 + minimum_value: 0 + uint64: + name: uint64 + typeof: integer + repr: np.uint64 + minimum_value: 0 + numeric: + name: numeric + typeof: float + repr: np.number + text: + name: text + typeof: string + utf: + name: utf + typeof: string + utf8: + name: utf8 + typeof: string + utf_8: + name: utf_8 + typeof: string + ascii: + name: ascii + typeof: string + bool: + name: bool + typeof: boolean + isodatetime: + name: isodatetime + typeof: datetime + repr: np.datetime64 +classes: + AnyType: + name: AnyType + description: Needed because some classes in hdmf-common are datasets without dtype + class_uri: linkml:Any diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.sparse.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.sparse.yaml new file mode 100644 index 0000000..e258d51 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.sparse.yaml @@ -0,0 +1,66 @@ +name: hdmf-common.sparse +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.sparse +version: 1.7.0 +imports: +- hdmf-common.base +- hdmf-common.nwb.language +default_prefix: hdmf-common.sparse/ +classes: + CSRMatrix: + name: CSRMatrix + description: A compressed sparse row matrix. Data are stored in the standard CSR + format, where column indices for row i are stored in indices[indptr[i]:indptr[i+1]] + and their corresponding values are stored in data[indptr[i]:indptr[i+1]]. + is_a: Container + attributes: + name: + name: name + range: string + required: true + shape: + name: shape + description: The shape (number of rows, number of columns) of this sparse + matrix. + range: uint + indices: + name: indices + description: The column indices. + array: + dimensions: + - alias: number_of_non_zero_values + range: uint + required: true + multivalued: false + indptr: + name: indptr + description: The row index pointer. + array: + dimensions: + - alias: number_of_rows_in_the_matrix_1 + range: uint + required: true + multivalued: false + data: + name: data + description: The non-zero values in the matrix. + range: CSRMatrix__data + required: true + multivalued: false + tree_root: true + CSRMatrix__data: + name: CSRMatrix__data + description: The non-zero values in the matrix. + attributes: + name: + name: name + ifabsent: string(data) + range: string + required: true + equals_string: data diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.table.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.table.yaml new file mode 100644 index 0000000..3dba25e --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/hdmf-common.table.yaml @@ -0,0 +1,185 @@ +name: hdmf-common.table +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-common +id: hdmf-common.table +version: 1.7.0 +imports: +- hdmf-common.base +- hdmf-common.nwb.language +default_prefix: hdmf-common.table/ +classes: + VectorData: + name: VectorData + description: An n-dimensional dataset representing a column of a DynamicTable. + If used without an accompanying VectorIndex, first dimension is along the rows + of the DynamicTable and each step along the first dimension is a cell of the + larger table. VectorData can also be used to represent a ragged array if paired + with a VectorIndex. This allows for storing arrays of varying length in a single + cell of the DynamicTable by indexing into this VectorData. The first vector + is at VectorData[0:VectorIndex[0]]. The second vector is at VectorData[VectorIndex[0]:VectorIndex[1]], + and so on. + is_a: Data + attributes: + name: + name: name + range: string + required: true + description: + name: description + description: Description of what these vectors represent. + range: text + array: + name: array + range: AnyType + any_of: + - array: + dimensions: + - alias: dim0 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - alias: dim2 + - array: + dimensions: + - alias: dim0 + - alias: dim1 + - alias: dim2 + - alias: dim3 + tree_root: true + VectorIndex: + name: VectorIndex + description: Used with VectorData to encode a ragged array. An array of indices + into the first dimension of the target VectorData, and forming a map between + the rows of a DynamicTable and the indices of the VectorData. The name of the + VectorIndex is expected to be the name of the target VectorData object followed + by "_index". + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + target: + name: target + description: Reference to the target dataset that this index applies to. + range: VectorData + tree_root: true + ElementIdentifiers: + name: ElementIdentifiers + description: A list of unique identifiers for values within a dataset, e.g. rows + of a DynamicTable. + is_a: Data + attributes: + name: + name: name + ifabsent: string(element_id) + range: string + required: true + tree_root: true + DynamicTableRegion: + name: DynamicTableRegion + description: DynamicTableRegion provides a link from one table to an index or + region of another. The `table` attribute is a link to another `DynamicTable`, + indicating which table is referenced, and the data is int(s) indicating the + row(s) (0-indexed) of the target array. `DynamicTableRegion`s can be used to + associate rows with repeated meta-data without data duplication. They can also + be used to create hierarchical relationships between multiple `DynamicTable`s. + `DynamicTableRegion` objects may be paired with a `VectorIndex` object to create + ragged references, so a single cell of a `DynamicTable` can reference many rows + of another `DynamicTable`. + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + table: + name: table + description: Reference to the DynamicTable object that this region applies + to. + range: DynamicTable + description: + name: description + description: Description of what this table region points to. + range: text + tree_root: true + DynamicTable: + name: DynamicTable + description: A group containing multiple datasets that are aligned on the first + dimension (Currently, this requirement if left up to APIs to check and enforce). + These datasets represent different columns in the table. Apart from a column + that contains unique identifiers for each row, there are no other required datasets. + Users are free to add any number of custom VectorData objects (columns) here. + DynamicTable also supports ragged array columns, where each element can be of + a different size. To add a ragged array column, use a VectorIndex type to index + the corresponding VectorData type. See documentation for VectorData and VectorIndex + for more details. Unlike a compound data type, which is analogous to storing + an array-of-structs, a DynamicTable can be thought of as a struct-of-arrays. + This provides an alternative structure to choose from when optimizing storage + for anticipated access patterns. Additionally, this type provides a way of creating + a table without having to define a compound type up front. Although this convenience + may be attractive, users should think carefully about how data will be accessed. + DynamicTable is more appropriate for column-centric access, whereas a dataset + with a compound type would be more appropriate for row-centric access. Finally, + data size should also be taken into account. For small tables, performance loss + may be an acceptable trade-off for the flexibility of a DynamicTable. + is_a: Container + attributes: + name: + name: name + range: string + required: true + colnames: + name: colnames + description: The names of the columns in this table. This should be used to + specify an order to the columns. + range: text + description: + name: description + description: Description of what is in this dynamic table. + range: text + id: + name: id + description: Array of unique identifiers for the rows of this dynamic table. + array: + dimensions: + - alias: num_rows + range: int + required: true + multivalued: false + vector_data: + name: vector_data + description: Vector columns, including index columns, of this dynamic table. + range: VectorData + required: false + multivalued: true + tree_root: true + AlignedDynamicTable: + name: AlignedDynamicTable + description: DynamicTable container that supports storing a collection of sub-tables. + Each sub-table is a DynamicTable itself that is aligned with the main table + by row index. I.e., all DynamicTables stored in this group MUST have the same + number of rows. This type effectively defines a 2-level table in which the main + data is stored in the main table implemented by this type and additional columns + of the table are grouped into categories, with each category being represented + by a separate DynamicTable stored within the group. + is_a: DynamicTable + attributes: + children: + name: children + multivalued: true + inlined: true + inlined_as_list: false + any_of: + - range: DynamicTable + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/namespace.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/namespace.yaml new file mode 100644 index 0000000..b689554 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_7_0/namespace.yaml @@ -0,0 +1,17 @@ +name: hdmf-common +annotations: + is_namespace: + tag: is_namespace + value: true + namespace: + tag: namespace + value: hdmf-common +description: Common data structures provided by HDMF +id: hdmf-common +version: 1.7.0 +imports: +- hdmf-common.base +- hdmf-common.table +- hdmf-common.sparse +- hdmf-common.nwb.language +default_prefix: hdmf-common/ diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_8_0/hdmf-common.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_8_0/hdmf-common.nwb.language.yaml index 68f0304..98c00e4 100644 --- a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_8_0/hdmf-common.nwb.language.yaml +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_common/v1_8_0/hdmf-common.nwb.language.yaml @@ -5,7 +5,7 @@ annotations: value: 'False' namespace: tag: namespace - value: core + value: hdmf-experimental description: Adapter objects to mimic the behavior of elements in the nwb-schema-language id: nwb.language imports: diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.experimental.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.experimental.yaml index c14e264..6b54542 100644 --- a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.experimental.yaml +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.experimental.yaml @@ -9,7 +9,7 @@ annotations: id: hdmf-experimental.experimental version: 0.1.0 imports: -- ../../hdmf_common/v1_5_0/namespace +- ../../hdmf_common/v1_4_0/namespace - hdmf-experimental.nwb.language default_prefix: hdmf-experimental.experimental/ classes: diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.nwb.language.yaml index 5bb0e2b..a884e44 100644 --- a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.nwb.language.yaml +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.nwb.language.yaml @@ -5,7 +5,7 @@ annotations: value: 'False' namespace: tag: namespace - value: core + value: hdmf-experimental description: Adapter objects to mimic the behavior of elements in the nwb-schema-language id: nwb.language imports: diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.resources.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.resources.yaml index 17a7d9d..89ffc2c 100644 --- a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.resources.yaml +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_1_0/hdmf-experimental.resources.yaml @@ -9,7 +9,7 @@ annotations: id: hdmf-experimental.resources version: 0.1.0 imports: -- ../../hdmf_common/v1_5_0/namespace +- ../../hdmf_common/v1_4_0/namespace - hdmf-experimental.nwb.language default_prefix: hdmf-experimental.resources/ classes: diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.experimental.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.experimental.yaml new file mode 100644 index 0000000..daf947b --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.experimental.yaml @@ -0,0 +1,31 @@ +name: hdmf-experimental.experimental +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-experimental +id: hdmf-experimental.experimental +version: 0.2.0 +imports: +- ../../hdmf_common/v1_5_1/namespace +- hdmf-experimental.nwb.language +default_prefix: hdmf-experimental.experimental/ +classes: + EnumData: + name: EnumData + description: Data that come from a fixed set of values. A data value of i corresponds + to the i-th value in the VectorData referenced by the 'elements' attribute. + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + elements: + name: elements + description: Reference to the VectorData object that contains the enumerable + elements + range: VectorData + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.nwb.language.yaml new file mode 100644 index 0000000..a884e44 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.nwb.language.yaml @@ -0,0 +1,109 @@ +name: hdmf-experimental.nwb.language +annotations: + is_namespace: + tag: is_namespace + value: 'False' + namespace: + tag: namespace + value: hdmf-experimental +description: Adapter objects to mimic the behavior of elements in the nwb-schema-language +id: nwb.language +imports: +- linkml:types +prefixes: + linkml: + prefix_prefix: linkml + prefix_reference: https://w3id.org/linkml +default_prefix: nwb.language/ +types: + float32: + name: float32 + typeof: float + repr: np.float32 + float64: + name: float64 + typeof: double + repr: np.float64 + long: + name: long + typeof: integer + repr: np.longlong + int64: + name: int64 + typeof: integer + repr: np.int64 + int: + name: int + typeof: integer + int32: + name: int32 + typeof: integer + repr: np.int32 + int16: + name: int16 + typeof: integer + repr: np.int16 + short: + name: short + typeof: integer + repr: np.int16 + int8: + name: int8 + typeof: integer + repr: np.int8 + uint: + name: uint + typeof: integer + repr: np.uint64 + minimum_value: 0 + uint32: + name: uint32 + typeof: integer + repr: np.uint32 + minimum_value: 0 + uint16: + name: uint16 + typeof: integer + repr: np.uint16 + minimum_value: 0 + uint8: + name: uint8 + typeof: integer + repr: np.uint8 + minimum_value: 0 + uint64: + name: uint64 + typeof: integer + repr: np.uint64 + minimum_value: 0 + numeric: + name: numeric + typeof: float + repr: np.number + text: + name: text + typeof: string + utf: + name: utf + typeof: string + utf8: + name: utf8 + typeof: string + utf_8: + name: utf_8 + typeof: string + ascii: + name: ascii + typeof: string + bool: + name: bool + typeof: boolean + isodatetime: + name: isodatetime + typeof: datetime + repr: np.datetime64 +classes: + AnyType: + name: AnyType + description: Needed because some classes in hdmf-common are datasets without dtype + class_uri: linkml:Any diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.resources.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.resources.yaml new file mode 100644 index 0000000..c2fc8d8 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/hdmf-experimental.resources.yaml @@ -0,0 +1,196 @@ +name: hdmf-experimental.resources +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-experimental +id: hdmf-experimental.resources +version: 0.2.0 +imports: +- ../../hdmf_common/v1_5_1/namespace +- hdmf-experimental.nwb.language +default_prefix: hdmf-experimental.resources/ +classes: + ExternalResources: + name: ExternalResources + description: 'A set of four tables for tracking external resource references in + a file. NOTE: this data type is in beta testing and is subject to change in + a later version.' + is_a: Container + attributes: + name: + name: name + range: string + required: true + keys: + name: keys + description: A table for storing user terms that are used to refer to external + resources. + range: ExternalResources__keys + required: true + multivalued: false + entities: + name: entities + description: A table for mapping user terms (i.e., keys) to resource entities. + range: ExternalResources__entities + required: true + multivalued: false + resources: + name: resources + description: A table for mapping user terms (i.e., keys) to resource entities. + range: ExternalResources__resources + required: true + multivalued: false + objects: + name: objects + description: A table for identifying which objects in a file contain references + to external resources. + range: ExternalResources__objects + required: true + multivalued: false + object_keys: + name: object_keys + description: A table for identifying which objects use which keys. + range: ExternalResources__object_keys + required: true + multivalued: false + tree_root: true + ExternalResources__keys: + name: ExternalResources__keys + description: A table for storing user terms that are used to refer to external + resources. + is_a: Data + attributes: + name: + name: name + ifabsent: string(keys) + range: string + required: true + equals_string: keys + key: + name: key + description: The user term that maps to one or more resources in the 'resources' + table. + range: text + required: true + multivalued: false + ExternalResources__entities: + name: ExternalResources__entities + description: A table for mapping user terms (i.e., keys) to resource entities. + is_a: Data + attributes: + name: + name: name + ifabsent: string(entities) + range: string + required: true + equals_string: entities + keys_idx: + name: keys_idx + description: The index to the key in the 'keys' table. + range: uint + required: true + multivalued: false + resources_idx: + name: resources_idx + description: The index into the 'resources' table + range: uint + required: true + multivalued: false + entity_id: + name: entity_id + description: The unique identifier entity. + range: text + required: true + multivalued: false + entity_uri: + name: entity_uri + description: The URI for the entity this reference applies to. This can be + an empty string. + range: text + required: true + multivalued: false + ExternalResources__resources: + name: ExternalResources__resources + description: A table for mapping user terms (i.e., keys) to resource entities. + is_a: Data + attributes: + name: + name: name + ifabsent: string(resources) + range: string + required: true + equals_string: resources + resource: + name: resource + description: The name of the resource. + range: text + required: true + multivalued: false + resource_uri: + name: resource_uri + description: The URI for the resource. This can be an empty string. + range: text + required: true + multivalued: false + ExternalResources__objects: + name: ExternalResources__objects + description: A table for identifying which objects in a file contain references + to external resources. + is_a: Data + attributes: + name: + name: name + ifabsent: string(objects) + range: string + required: true + equals_string: objects + object_id: + name: object_id + description: The UUID for the object. + range: text + required: true + multivalued: false + relative_path: + name: relative_path + description: The relative path from the container with the object_id to the + dataset or attribute with the value(s) that is associated with an external + resource. This can be an empty string if the container is a dataset which + contains the value(s) that is associated with an external resource. + range: text + required: true + multivalued: false + field: + name: field + description: The field of the compound data type using an external resource. + This is used only if the dataset or attribute is a compound data type; otherwise + this should be an empty string. + range: text + required: true + multivalued: false + ExternalResources__object_keys: + name: ExternalResources__object_keys + description: A table for identifying which objects use which keys. + is_a: Data + attributes: + name: + name: name + ifabsent: string(object_keys) + range: string + required: true + equals_string: object_keys + objects_idx: + name: objects_idx + description: The index to the 'objects' table for the object that holds the + key. + range: uint + required: true + multivalued: false + keys_idx: + name: keys_idx + description: The index to the 'keys' table for the key. + range: uint + required: true + multivalued: false diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/namespace.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/namespace.yaml new file mode 100644 index 0000000..6a311e0 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_2_0/namespace.yaml @@ -0,0 +1,17 @@ +name: hdmf-experimental +annotations: + is_namespace: + tag: is_namespace + value: true + namespace: + tag: namespace + value: hdmf-experimental +description: Experimental data structures provided by HDMF. These are not guaranteed + to be available in the future. +id: hdmf-experimental +version: 0.2.0 +imports: +- hdmf-experimental.experimental +- hdmf-experimental.resources +- hdmf-experimental.nwb.language +default_prefix: hdmf-experimental/ diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.experimental.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.experimental.yaml new file mode 100644 index 0000000..fb98004 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.experimental.yaml @@ -0,0 +1,31 @@ +name: hdmf-experimental.experimental +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-experimental +id: hdmf-experimental.experimental +version: 0.3.0 +imports: +- ../../hdmf_common/v1_6_0/namespace +- hdmf-experimental.nwb.language +default_prefix: hdmf-experimental.experimental/ +classes: + EnumData: + name: EnumData + description: Data that come from a fixed set of values. A data value of i corresponds + to the i-th value in the VectorData referenced by the 'elements' attribute. + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + elements: + name: elements + description: Reference to the VectorData object that contains the enumerable + elements + range: VectorData + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.nwb.language.yaml new file mode 100644 index 0000000..a884e44 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.nwb.language.yaml @@ -0,0 +1,109 @@ +name: hdmf-experimental.nwb.language +annotations: + is_namespace: + tag: is_namespace + value: 'False' + namespace: + tag: namespace + value: hdmf-experimental +description: Adapter objects to mimic the behavior of elements in the nwb-schema-language +id: nwb.language +imports: +- linkml:types +prefixes: + linkml: + prefix_prefix: linkml + prefix_reference: https://w3id.org/linkml +default_prefix: nwb.language/ +types: + float32: + name: float32 + typeof: float + repr: np.float32 + float64: + name: float64 + typeof: double + repr: np.float64 + long: + name: long + typeof: integer + repr: np.longlong + int64: + name: int64 + typeof: integer + repr: np.int64 + int: + name: int + typeof: integer + int32: + name: int32 + typeof: integer + repr: np.int32 + int16: + name: int16 + typeof: integer + repr: np.int16 + short: + name: short + typeof: integer + repr: np.int16 + int8: + name: int8 + typeof: integer + repr: np.int8 + uint: + name: uint + typeof: integer + repr: np.uint64 + minimum_value: 0 + uint32: + name: uint32 + typeof: integer + repr: np.uint32 + minimum_value: 0 + uint16: + name: uint16 + typeof: integer + repr: np.uint16 + minimum_value: 0 + uint8: + name: uint8 + typeof: integer + repr: np.uint8 + minimum_value: 0 + uint64: + name: uint64 + typeof: integer + repr: np.uint64 + minimum_value: 0 + numeric: + name: numeric + typeof: float + repr: np.number + text: + name: text + typeof: string + utf: + name: utf + typeof: string + utf8: + name: utf8 + typeof: string + utf_8: + name: utf_8 + typeof: string + ascii: + name: ascii + typeof: string + bool: + name: bool + typeof: boolean + isodatetime: + name: isodatetime + typeof: datetime + repr: np.datetime64 +classes: + AnyType: + name: AnyType + description: Needed because some classes in hdmf-common are datasets without dtype + class_uri: linkml:Any diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.resources.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.resources.yaml new file mode 100644 index 0000000..350ef24 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/hdmf-experimental.resources.yaml @@ -0,0 +1,199 @@ +name: hdmf-experimental.resources +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-experimental +id: hdmf-experimental.resources +version: 0.3.0 +imports: +- ../../hdmf_common/v1_6_0/namespace +- hdmf-experimental.nwb.language +default_prefix: hdmf-experimental.resources/ +classes: + ExternalResources: + name: ExternalResources + description: 'A set of five tables for tracking external resource references in + a file. NOTE: this data type is experimental and is subject to change in a later + version.' + is_a: Container + attributes: + name: + name: name + range: string + required: true + keys: + name: keys + description: A table for storing user terms that are used to refer to external + resources. + range: ExternalResources__keys + required: true + multivalued: false + files: + name: files + description: A table for storing object ids of files used in external resources. + range: ExternalResources__files + required: true + multivalued: false + entities: + name: entities + description: A table for mapping user terms (i.e., keys) to resource entities. + range: ExternalResources__entities + required: true + multivalued: false + objects: + name: objects + description: A table for identifying which objects in a file contain references + to external resources. + range: ExternalResources__objects + required: true + multivalued: false + object_keys: + name: object_keys + description: A table for identifying which objects use which keys. + range: ExternalResources__object_keys + required: true + multivalued: false + tree_root: true + ExternalResources__keys: + name: ExternalResources__keys + description: A table for storing user terms that are used to refer to external + resources. + is_a: Data + attributes: + name: + name: name + ifabsent: string(keys) + range: string + required: true + equals_string: keys + key: + name: key + description: The user term that maps to one or more resources in the `resources` + table, e.g., "human". + range: text + required: true + multivalued: false + ExternalResources__files: + name: ExternalResources__files + description: A table for storing object ids of files used in external resources. + is_a: Data + attributes: + name: + name: name + ifabsent: string(files) + range: string + required: true + equals_string: files + file_object_id: + name: file_object_id + description: The object id (UUID) of a file that contains objects that refers + to external resources. + range: text + required: true + multivalued: false + ExternalResources__entities: + name: ExternalResources__entities + description: A table for mapping user terms (i.e., keys) to resource entities. + is_a: Data + attributes: + name: + name: name + ifabsent: string(entities) + range: string + required: true + equals_string: entities + keys_idx: + name: keys_idx + description: The row index to the key in the `keys` table. + range: uint + required: true + multivalued: false + entity_id: + name: entity_id + description: The compact uniform resource identifier (CURIE) of the entity, + in the form [prefix]:[unique local identifier], e.g., 'NCBI_TAXON:9606'. + range: text + required: true + multivalued: false + entity_uri: + name: entity_uri + description: The URI for the entity this reference applies to. This can be + an empty string. e.g., https://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?mode=info&id=9606 + range: text + required: true + multivalued: false + ExternalResources__objects: + name: ExternalResources__objects + description: A table for identifying which objects in a file contain references + to external resources. + is_a: Data + attributes: + name: + name: name + ifabsent: string(objects) + range: string + required: true + equals_string: objects + files_idx: + name: files_idx + description: The row index to the file in the `files` table containing the + object. + range: uint + required: true + multivalued: false + object_id: + name: object_id + description: The object id (UUID) of the object. + range: text + required: true + multivalued: false + object_type: + name: object_type + description: The data type of the object. + range: text + required: true + multivalued: false + relative_path: + name: relative_path + description: The relative path from the data object with the `object_id` to + the dataset or attribute with the value(s) that is associated with an external + resource. This can be an empty string if the object is a dataset that contains + the value(s) that is associated with an external resource. + range: text + required: true + multivalued: false + field: + name: field + description: The field within the compound data type using an external resource. + This is used only if the dataset or attribute is a compound data type; otherwise + this should be an empty string. + range: text + required: true + multivalued: false + ExternalResources__object_keys: + name: ExternalResources__object_keys + description: A table for identifying which objects use which keys. + is_a: Data + attributes: + name: + name: name + ifabsent: string(object_keys) + range: string + required: true + equals_string: object_keys + objects_idx: + name: objects_idx + description: The row index to the object in the `objects` table that holds + the key + range: uint + required: true + multivalued: false + keys_idx: + name: keys_idx + description: The row index to the key in the `keys` table. + range: uint + required: true + multivalued: false diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/namespace.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/namespace.yaml new file mode 100644 index 0000000..fe62e64 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_3_0/namespace.yaml @@ -0,0 +1,17 @@ +name: hdmf-experimental +annotations: + is_namespace: + tag: is_namespace + value: true + namespace: + tag: namespace + value: hdmf-experimental +description: Experimental data structures provided by HDMF. These are not guaranteed + to be available in the future. +id: hdmf-experimental +version: 0.3.0 +imports: +- hdmf-experimental.experimental +- hdmf-experimental.resources +- hdmf-experimental.nwb.language +default_prefix: hdmf-experimental/ diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.experimental.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.experimental.yaml new file mode 100644 index 0000000..31c2867 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.experimental.yaml @@ -0,0 +1,31 @@ +name: hdmf-experimental.experimental +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-experimental +id: hdmf-experimental.experimental +version: 0.4.0 +imports: +- ../../hdmf_common/v1_7_0/namespace +- hdmf-experimental.nwb.language +default_prefix: hdmf-experimental.experimental/ +classes: + EnumData: + name: EnumData + description: Data that come from a fixed set of values. A data value of i corresponds + to the i-th value in the VectorData referenced by the 'elements' attribute. + is_a: VectorData + attributes: + name: + name: name + range: string + required: true + elements: + name: elements + description: Reference to the VectorData object that contains the enumerable + elements + range: VectorData + tree_root: true diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.nwb.language.yaml new file mode 100644 index 0000000..a884e44 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.nwb.language.yaml @@ -0,0 +1,109 @@ +name: hdmf-experimental.nwb.language +annotations: + is_namespace: + tag: is_namespace + value: 'False' + namespace: + tag: namespace + value: hdmf-experimental +description: Adapter objects to mimic the behavior of elements in the nwb-schema-language +id: nwb.language +imports: +- linkml:types +prefixes: + linkml: + prefix_prefix: linkml + prefix_reference: https://w3id.org/linkml +default_prefix: nwb.language/ +types: + float32: + name: float32 + typeof: float + repr: np.float32 + float64: + name: float64 + typeof: double + repr: np.float64 + long: + name: long + typeof: integer + repr: np.longlong + int64: + name: int64 + typeof: integer + repr: np.int64 + int: + name: int + typeof: integer + int32: + name: int32 + typeof: integer + repr: np.int32 + int16: + name: int16 + typeof: integer + repr: np.int16 + short: + name: short + typeof: integer + repr: np.int16 + int8: + name: int8 + typeof: integer + repr: np.int8 + uint: + name: uint + typeof: integer + repr: np.uint64 + minimum_value: 0 + uint32: + name: uint32 + typeof: integer + repr: np.uint32 + minimum_value: 0 + uint16: + name: uint16 + typeof: integer + repr: np.uint16 + minimum_value: 0 + uint8: + name: uint8 + typeof: integer + repr: np.uint8 + minimum_value: 0 + uint64: + name: uint64 + typeof: integer + repr: np.uint64 + minimum_value: 0 + numeric: + name: numeric + typeof: float + repr: np.number + text: + name: text + typeof: string + utf: + name: utf + typeof: string + utf8: + name: utf8 + typeof: string + utf_8: + name: utf_8 + typeof: string + ascii: + name: ascii + typeof: string + bool: + name: bool + typeof: boolean + isodatetime: + name: isodatetime + typeof: datetime + repr: np.datetime64 +classes: + AnyType: + name: AnyType + description: Needed because some classes in hdmf-common are datasets without dtype + class_uri: linkml:Any diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.resources.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.resources.yaml new file mode 100644 index 0000000..8768e73 --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/hdmf-experimental.resources.yaml @@ -0,0 +1,222 @@ +name: hdmf-experimental.resources +annotations: + is_namespace: + tag: is_namespace + value: false + namespace: + tag: namespace + value: hdmf-experimental +id: hdmf-experimental.resources +version: 0.4.0 +imports: +- ../../hdmf_common/v1_7_0/namespace +- hdmf-experimental.nwb.language +default_prefix: hdmf-experimental.resources/ +classes: + ExternalResources: + name: ExternalResources + description: 'A set of five tables for tracking external resource references in + a file. NOTE: this data type is experimental and is subject to change in a later + version.' + is_a: Container + attributes: + name: + name: name + range: string + required: true + keys: + name: keys + description: A table for storing user terms that are used to refer to external + resources. + range: ExternalResources__keys + required: true + multivalued: false + files: + name: files + description: A table for storing object ids of files used in external resources. + range: ExternalResources__files + required: true + multivalued: false + entities: + name: entities + description: A table for mapping user terms (i.e., keys) to resource entities. + range: ExternalResources__entities + required: true + multivalued: false + objects: + name: objects + description: A table for identifying which objects in a file contain references + to external resources. + range: ExternalResources__objects + required: true + multivalued: false + object_keys: + name: object_keys + description: A table for identifying which objects use which keys. + range: ExternalResources__object_keys + required: true + multivalued: false + entity_keys: + name: entity_keys + description: A table for identifying which keys use which entity. + range: ExternalResources__entity_keys + required: true + multivalued: false + tree_root: true + ExternalResources__keys: + name: ExternalResources__keys + description: A table for storing user terms that are used to refer to external + resources. + is_a: Data + attributes: + name: + name: name + ifabsent: string(keys) + range: string + required: true + equals_string: keys + key: + name: key + description: The user term that maps to one or more resources in the `resources` + table, e.g., "human". + range: text + required: true + multivalued: false + ExternalResources__files: + name: ExternalResources__files + description: A table for storing object ids of files used in external resources. + is_a: Data + attributes: + name: + name: name + ifabsent: string(files) + range: string + required: true + equals_string: files + file_object_id: + name: file_object_id + description: The object id (UUID) of a file that contains objects that refers + to external resources. + range: text + required: true + multivalued: false + ExternalResources__entities: + name: ExternalResources__entities + description: A table for mapping user terms (i.e., keys) to resource entities. + is_a: Data + attributes: + name: + name: name + ifabsent: string(entities) + range: string + required: true + equals_string: entities + entity_id: + name: entity_id + description: The compact uniform resource identifier (CURIE) of the entity, + in the form [prefix]:[unique local identifier], e.g., 'NCBI_TAXON:9606'. + range: text + required: true + multivalued: false + entity_uri: + name: entity_uri + description: The URI for the entity this reference applies to. This can be + an empty string. e.g., https://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?mode=info&id=9606 + range: text + required: true + multivalued: false + ExternalResources__objects: + name: ExternalResources__objects + description: A table for identifying which objects in a file contain references + to external resources. + is_a: Data + attributes: + name: + name: name + ifabsent: string(objects) + range: string + required: true + equals_string: objects + files_idx: + name: files_idx + description: The row index to the file in the `files` table containing the + object. + range: uint + required: true + multivalued: false + object_id: + name: object_id + description: The object id (UUID) of the object. + range: text + required: true + multivalued: false + object_type: + name: object_type + description: The data type of the object. + range: text + required: true + multivalued: false + relative_path: + name: relative_path + description: The relative path from the data object with the `object_id` to + the dataset or attribute with the value(s) that is associated with an external + resource. This can be an empty string if the object is a dataset that contains + the value(s) that is associated with an external resource. + range: text + required: true + multivalued: false + field: + name: field + description: The field within the compound data type using an external resource. + This is used only if the dataset or attribute is a compound data type; otherwise + this should be an empty string. + range: text + required: true + multivalued: false + ExternalResources__object_keys: + name: ExternalResources__object_keys + description: A table for identifying which objects use which keys. + is_a: Data + attributes: + name: + name: name + ifabsent: string(object_keys) + range: string + required: true + equals_string: object_keys + objects_idx: + name: objects_idx + description: The row index to the object in the `objects` table that holds + the key + range: uint + required: true + multivalued: false + keys_idx: + name: keys_idx + description: The row index to the key in the `keys` table. + range: uint + required: true + multivalued: false + ExternalResources__entity_keys: + name: ExternalResources__entity_keys + description: A table for identifying which keys use which entity. + is_a: Data + attributes: + name: + name: name + ifabsent: string(entity_keys) + range: string + required: true + equals_string: entity_keys + entities_idx: + name: entities_idx + description: The row index to the entity in the `entities` table. + range: uint + required: true + multivalued: false + keys_idx: + name: keys_idx + description: The row index to the key in the `keys` table. + range: uint + required: true + multivalued: false diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/namespace.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/namespace.yaml new file mode 100644 index 0000000..a48814e --- /dev/null +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_4_0/namespace.yaml @@ -0,0 +1,17 @@ +name: hdmf-experimental +annotations: + is_namespace: + tag: is_namespace + value: true + namespace: + tag: namespace + value: hdmf-experimental +description: Experimental data structures provided by HDMF. These are not guaranteed + to be available in the future. +id: hdmf-experimental +version: 0.4.0 +imports: +- hdmf-experimental.experimental +- hdmf-experimental.resources +- hdmf-experimental.nwb.language +default_prefix: hdmf-experimental/ diff --git a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_5_0/hdmf-experimental.nwb.language.yaml b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_5_0/hdmf-experimental.nwb.language.yaml index 5bb0e2b..a884e44 100644 --- a/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_5_0/hdmf-experimental.nwb.language.yaml +++ b/nwb_linkml/src/nwb_linkml/schema/linkml/hdmf_experimental/v0_5_0/hdmf-experimental.nwb.language.yaml @@ -5,7 +5,7 @@ annotations: value: 'False' namespace: tag: namespace - value: core + value: hdmf-experimental description: Adapter objects to mimic the behavior of elements in the nwb-schema-language id: nwb.language imports: diff --git a/nwb_linkml/tests/fixtures.py b/nwb_linkml/tests/fixtures.py index 092ba60..ee4236b 100644 --- a/nwb_linkml/tests/fixtures.py +++ b/nwb_linkml/tests/fixtures.py @@ -90,11 +90,12 @@ def nwb_core_fixture(request) -> NamespacesAdapter: return nwb_core + @pytest.fixture(scope="session") def nwb_core_linkml(nwb_core_fixture, tmp_output_dir) -> LinkMLSchemaBuild: provider = LinkMLProvider(tmp_output_dir, allow_repo=False, verbose=False) result = provider.build(ns_adapter=nwb_core_fixture, force=True) - return result['core'] + return result["core"] @pytest.fixture(scope="session") @@ -104,13 +105,10 @@ def nwb_core_module(nwb_core_linkml: LinkMLSchemaBuild, tmp_output_dir) -> Modul """ provider = PydanticProvider(tmp_output_dir, verbose=False) result = provider.build(nwb_core_linkml.namespace, force=True) - mod = provider.get('core', version=nwb_core_linkml.version, allow_repo=False) + mod = provider.get("core", version=nwb_core_linkml.version, allow_repo=False) return mod - - - @pytest.fixture(scope="session") def data_dir() -> Path: path = Path(__file__).parent.resolve() / "data" diff --git a/nwb_linkml/tests/test_includes/test_hdmf.py b/nwb_linkml/tests/test_includes/test_hdmf.py index 572a651..b4da94b 100644 --- a/nwb_linkml/tests/test_includes/test_hdmf.py +++ b/nwb_linkml/tests/test_includes/test_hdmf.py @@ -32,6 +32,5 @@ def electrical_series() -> Tuple["ElectricalSeries", "NWBFileGeneralExtracellula id=np.arange(0, n_electrodes), x=np.arange(0, n_electrodes), y=np.arange(n_electrodes, n_electrodes * 2), - group=[electrode_group]*n_electrodes, - + group=[electrode_group] * n_electrodes, )