Index > SPARC queries Edit on GitHub

SPARC queries

Table of Contents

Data queries

Datasets

Dataset Predicates

SELECT DISTINCT
?p
# ?pp
# ?po
WHERE {
  ?dataset a sparc:Dataset .
  ?dataset ?p ?o .
  # ?p ?pp ?po .
} ORDER BY ?p

Electrophysiology datasets

SELECT DISTINCT
?d
?label
?approach
WHERE {
  VALUES ?match { "electrophysiology" }
  ?d a sparc:Dataset ;
  rdfs:label ?l ;
  TEMP:hasExperimentalApproach ?approach .
  FILTER (str(?approach) = str(?match))
  BIND(substr(str(?l), 0, 40) AS ?label)
  # TODO lift and pull filetypes
}

Datasets by contents updated time

SELECT DISTINCT
?dataset
?ut
(substr(str(?l), 0, 40) as ?label)
WHERE {
  ?dataset a sparc:Dataset .
  ?dataset TEMP:contentsWereUpdatedAtTime ?ut .
  ?dataset rdfs:label ?l .
} ORDER BY DESC(?ut)

Human dataset queries

import rdflib
from pyontutils.core import OntResIri
from pyontutils.namespaces import sparc, TEMP, dc, rdfs

ori = OntResIri('https://cassava.ucsd.edu/sparc/preview/exports/curation-export.ttl')
g = ori.graph
gns = g.namespace_manager

def fmt(s, u):
    return f'[[{u}][{s.n3(gns)}]]'

species = set([fmt(do, urih) for s, p, o in g
              if isinstance(o, rdflib.Literal) and
              ('human' in o.lower() or 'homo' in o.lower()) and
              p == sparc.animalSubjectIsOfSpecies
              for do in g[s:TEMP.hasDerivedInformationAsParticipant]
              for urih in g[do:TEMP.hasUriHuman]])

hlabel = set([fmt(s, urih) for s, p, o in g
             if isinstance(o, rdflib.Literal) and
             ('human' in o.lower() or 'homo' in o.lower()) and
             p == rdfs.label
             for urih in g[s:TEMP.hasUriHuman]])

htitle = set([fmt(s, urih) for s, p, o in g
              if isinstance(o, rdflib.Literal) and
              ('human' in o.lower() or 'homo' in o.lower()) and
              p == dc.title
              for urih in g[s:TEMP.hasUriHuman]])

htd = set([fmt(s, urih) for s, p, o in g
           if isinstance(o, rdflib.Literal) and
           ('human' in o.lower() or 'homo' in o.lower()) and
           (p == dc.title or p == dc.description)
           for urih in g[s:TEMP.hasUriHuman]])

counts = dict(species=len(human),
              label=len(hlabel),
              title=len(htitle),
              title_and_desc=len(htd))

[print(_ + r' \\') for _ in ['species n= ' + str(counts['species'])] +
sorted(species) +
['label n= ' + str(counts['label'])] +
sorted(hlabel) +
['title n= ' + str(counts['title'])] +
sorted(htitle) +
['td n= ' + str(counts['title_and_desc'])] +
sorted(htd)]

Test

Originally from sparcur.reports.SparqlQueries.

  • Dataset about
    SELECT DISTINCT
    ?dataset
    ?about
    WHERE {
      ?type rdfs:subClassOf* sparc:Resource .
      ?dataset rdf:type ?type .
      ?dataset isAbout: ?about .
    }
    LIMIT ?limit
    
  • Dataset subjects
    SELECT DISTINCT
    ?dataset
    ?subj
    WHERE {
        ?startsubj a sparc:Subject .
        ?startsubj TEMP:hasDerivedInformationAsParticipant ?dataset .
        ?subj a sparc:Subject .
        ?subj  TEMP:hasDerivedInformationAsParticipant ?dataset .
    }
    LIMIT ?limit
    
  • Dataset groups
    SELECT DISTINCT
    ?dataset
    ?group
    ?subj
    WHERE {
        ?startsubj TEMP:hasDerivedInformationAsParticipant ?dataset .
        ?subj  TEMP:hasDerivedInformationAsParticipant ?dataset .
        ?subj  TEMP:hasAssignedGroup ?group .
    } LIMIT ?limit
    
  • Dataset bundle
    SELECT DISTINCT
    ?dataset
    WHERE {
        ?startdataset TEMP:collectionTitle ?string .
        ?dataset  TEMP:collectionTitle ?string .
    }
    LIMIT ?limit
    
  • Dataset subject species
    SELECT DISTINCT
    ?dataset
    ?species
    WHERE {
        ?dataset TEMP:isAboutParticipant ?subject .
        ?subject sparc:animalSubjectIsOfSpecies ?species .
        FILTER ( CONTAINS(str(?species), "human")
              || CONTAINS(str(?species), "homo sapiens")
              || ?species = NCBITaxon:9606 )
    }
    LIMIT ?limit
    
  • Dataset human subjects and samples
    SELECT DISTINCT
    ?dataset
    ?subject
    ?subject_meta
    ?sample
    ?sample_meta
    ?species
    WHERE {
        ?dataset TEMP:isAboutParticipant ?subject .
        ?subject sparc:animalSubjectIsOfSpecies ?species .
        ?subject ?sup ?subject_meta .
        FILTER ( CONTAINS(str(?species), "human") || CONTAINS(str(?species), "homo sapiens") || ?species = NCBITaxon:9606 )
        ?sample TEMP:wasDerivedFromSubject ?subject .
        ?sample ?sap ?sample_meta .
    }
    LIMIT ?limit
    
  • Dataset milestone completion date
    SELECT DISTINCT
    ?dataset
    ?date
    WHERE {
        ?dataset TEMP:milestoneCompletionDate ?date .
    }
    ORDER BY ASC(?date)
    LIMIT ?limit
    
  • Award affiliations

    Can we see the multi-institutional nature of SPARC collaborations?

    SELECT DISTINCT
    ?award
    ?affiliation
    (str(?affil_l) as ?al)
    WHERE {
        ?dataset TEMP:hasAwardNumber ?award .
        ?contributor TEMP:contributorTo ?dataset .
        ?contributor TEMP:hasAffiliation ?affiliation .
        ?affiliation rdfs:label ?affil_l .
        # FILTER isUri(?affiliation)
    }
    ORDER BY ASC(?award) ASC(?al)
    LIMIT ?limit
    

Protocols

Protocol general report

How to interpret this report.

The rows are ordered by

  1. whether there is a dataset directly associated with the protocol id
  2. whether the protocol has a human readable uri.
  3. by the number of protcur annotations that have been made on the protocol

If there are zero protcur annotations it usually means that only the minimal protocol curation workflow was completed. If there the number of protocol annotations is null it means that no annotations of any kind have been made on that protocol. This can only happen for protocols that come from the dataset description file.

If the dataset is null and there are annotations it usually means that the protocol is transitively related to a dataset. There are annotations on the protocols that link them directly to the dataset in question but we are not currently pulling them into the knowledge graph. It also means that the protocol url in question was not listed in the dataset description file but may have been listed in another protocol or in a collection of protocols.

If hasUri is false and dataset is not null then it usually means that the protocol has been deleted or that something else has gone wrong. If dataset is null and hasUri is null it just means that the protocol is present only in protcur.ttl and the additional identifiers have not been processed.

select distinct
?protocol
?n
(sample(?dataset) AS ?datasetx)
?doi_protocol
(sample(bound(?urih_protocol)) as ?hasUri)
(sample(bound(?some_child)) as ?hasComplexAnnos)
where {
  ?protocol a sparc:Protocol .
  optional { ?protocol TEMP:hasNumberOfProtcurAnnotations ?n }
  optional { ?dataset TEMP:hasProtocol ?protocol }
  optional { ?protocol TEMP:hasDoi ?doi_protocol }
  optional { ?protocol TEMP:hasUriHuman ?urih_protocol }
  optional { ?protocol ?some_predicate ?annotation .
             ?annotation TEMP:protcurChildren ?some_child }
}
group by ?protocol ?n ?doi_protocol ?hasUri
order by desc(bound(?datasetx)) asc(?hasUri) desc(?n)

./reports/protocol-report.csv

Total protocols

SELECT (COUNT(DISTINCT ?protocol) AS ?count)
WHERE { ?protocol a sparc:Protocol . }

Total protocols with at least one protcur annotation

SELECT (COUNT(*) as ?count) {
  <<sparql-protocols-at-least-one-protc>>
}}
SELECT (COUNT(*) as ?count) {
  <<sparql-protocols-pio-api-at-least-one-protc>>
}
select distinct
?protocol ?anno_count
where {
  values ?some_predicate {
    TEMP:protocolEmploysTechnique
    TEMP:protocolInvolvesAction
    TEMP:protocolInvolvesAspect
    TEMP:protocolInvolvesBlackBox
    TEMP:protocolInvolvesBlackBoxComponent
    TEMP:protocolInvolvesInput
    TEMP:protocolInvolvesInvariant
  }
  ?protocol a sparc:Protocol ;
    ?some_predicate ?annotation ;  # this line an the filter should wind up with the same number TODO add a test to check
    TEMP:hasNumberOfProtcurAnnotations ?anno_count .
  FILTER (?anno_count > 0 )
  FILTER CONTAINS(str(?protocol), "protocols.io/api")
  #FILTER CONTAINS(str(?protocol), "protocols.io")
}
grep -oE 'TEMP:protocol[^\ ]+' protcur.ttl | sort -u
select distinct
?protocol ?anno_count
where {
  values ?some_predicate {
    TEMP:protocolEmploysTechnique
    TEMP:protocolInvolvesAction
    TEMP:protocolInvolvesAspect
    TEMP:protocolInvolvesBlackBox
    TEMP:protocolInvolvesBlackBoxComponent
    TEMP:protocolInvolvesInput
    TEMP:protocolInvolvesInvariant
  }
  ?protocol a sparc:Protocol ;
    ?some_predicate ?annotation ;  # this line an the filter should wind up with the same number TODO add a test to check
    TEMP:hasNumberOfProtcurAnnotations ?anno_count .
  FILTER (?anno_count > 0 )

Published protocols with complex protcur annotations

SELECT (COUNT(*) as ?count) {
  <<sparql-complex-protocols>>
}
SELECT (COUNT(*) as ?count) {
  <<sparql-complex-protocols-published>>
}
<<sparql-complex-protocols>>
    ?protocol TEMP:hasDoi ?doi .
}
select distinct
?protocol ?anno_count
where {
    ?annotation TEMP:protcurChildren ?some_child .
    ?protocol a sparc:Protocol ;
              ?some_predicate ?annotation ;
              TEMP:hasNumberOfProtcurAnnotations ?anno_count .

Conditions studied

Experimental groups

WHERE {
    ?startsubj TEMP:hasDerivedInformationAsParticipant ?dataset .
    ?subj  TEMP:hasDerivedInformationAsParticipant ?dataset .
    ?subj  TEMP:hasAssignedGroup ?group .
}
SELECT DISTINCT
?dataset
?group
?subj
WHERE {
    ?startsubj TEMP:hasDerivedInformationAsParticipant ?dataset .
    ?subj  TEMP:hasDerivedInformationAsParticipant ?dataset .
    ?subj  TEMP:hasAssignedGroup ?group .
}
ORDER BY ASC(?dataset) ASC(?group) LIMIT ?limit
SELECT DISTINCT
?group
?subj
WHERE {
    ?startsubj TEMP:hasDerivedInformationAsParticipant ?dataset .
    ?subj  TEMP:hasDerivedInformationAsParticipant ?dataset .
    ?subj  TEMP:hasAssignedGroup ?group .
}
ORDER BY ASC(?group) LIMIT ?limit

Techniques

SELECT DISTINCT
?dataset
?technique
# ?protocol
# ?technique_p
WHERE {
  { ?dataset a sparc:Dataset .
    ?dataset TEMP:protocolEmploysTechnique ?technique .
    # ?technique rdfs:label ?tl
  }
  UNION
  { ?dataset TEMP:hasProtocol ?protocol .
    ?protocol TEMP:protocolEmploysTechnique ?technique .
    # ?technique rdfs:label ?tl
  }
}
LIMIT ?limit

Test

Originally from sparcur.reports.SparqlQueries.

  • Protocol techniques
    SELECT DISTINCT
    ?protocol
    ?technique
    WHERE {
        ?protocol rdf:type sparc:Protocol .
        ?protocol TEMP:protocolEmploysTechnique ?technique .
    }
    LIMIT ?limit
    
  • Protocol aspects
    SELECT DISTINCT
    ?protocol
    ?aspect
    WHERE {
        ?protocol rdf:type sparc:Protocol .
        ?protocol TEMP:protocolInvolvesAspect ?ast .
        ?ast rdf:type protcur:aspect .
        ?ast TEMP:hasValue ?aspect .
    }
    LIMIT ?limit
    
  • Protocol inputs
    SELECT DISTINCT
    ?protocol
    ?ast_in
    ?input
    WHERE {
        ?protocol rdf:type sparc:Protocol .
        ?protocol TEMP:protocolInvolvesInput ?ast_in .
        ?ast_in rdf:type protcur:input .
        ?ast_in TEMP:hasValue ?input .
    }
    LIMIT ?limit
    
  • Protocol species dose
    SELECT DISTINCT
    ?dataset
    ?protocol
    
    (str(?label_drug) as ?l_drug)
    ?value_lt
    WHERE {
        VALUES ?t {protcur:invariant protcur:parameter} .
        ?ast_inv a ?t .
        ?ast_inv TEMP:hasValue ?bnode .
        ?bnode rdf:value ?value_lt .
        #?bnode TEMP:hasUnit unit:milligram%20%2F%20kilogram . # seems like the %20 etc. break curies?
        ?bnode TEMP:hasUnit <http://uri.interlex.org/tgbugs/uris/readable/aspect/unit/milligram%20%2F%20kilogram> .
        FILTER (?value_lt < ?limit)
    
        ?ast_drug a protcur:input .
        ?ast_drug TEMP:hasValue ?drug .
                                ?drug rdfs:label ?label_drug .
        ?ast_drug TEMP:protcurChildren+ ?ast_child .
        ?ast_child TEMP:hasValue ?bnode .
    
        ?protocol a sparc:Protocol .
        ?protocol TEMP:protocolInvolvesInput ?ast_drug .
    
        ?protocol TEMP:protocolInvolvesInput ?ast_in_sp .
        ?ast_in_sp rdf:type protcur:input .
        ?ast_in_sp TEMP:hasValue ?species .
    
        OPTIONAL { ?dataset TEMP:hasProtocol ?protocol } .
    
    }
    ORDER BY ?label_input ?value_lt
    
  • Protocols with known anaesthetic or analgesic roles
    select distinct
    ?pro
    ?ana
    ?l
    ?u
    ?v
    where {
      ?pro TEMP:protocolInvolvesInput ?anno .
      ?anno TEMP:hasValue ?ana .
      ?anno TEMP:protcurChildren ?ad .
      ?ad TEMP:hasValue ?asp .
      ?ad TEMP:protcurChildren ?ai .
      ?ai TEMP:hasValue ?bn .
      ?bn TEMP:hasUnit ?u .
      ?bn rdf:value ?v .
      ?ana rdfs:label ?l .
      ?ana ?p ?b .
      ?b owl:onProperty RO:0000087 .
      ?b owl:someValuesFrom ?o .
      ?o rdfs:subClassOf* ?role .
      values ?asp {asp:dose asp:percent-volume} .
      values ?role {CHEBI:38867 CHEBI:35480} .
    } order by ?l
    

Datasets following same protocol with different subjects

Completeness

SELECT DISTINCT
?dataset
?completeness
WHERE {
  ?dataset a sparc:Dataset .
  ?dataset TEMP:completenessOfDataset ?completeness .
}
LIMIT ?limit

Dataset collections

SELECT DISTINCT
?title
?dataset
WHERE {
    ?startdataset TEMP:collectionTitle ?title .
    ?dataset  TEMP:collectionTitle ?title .
}
ORDER BY ASC(?title)
LIMIT ?limit

Subjects

Members

Show me all of the experimental subjects that a dataset contains information about.

SELECT DISTINCT
?dataset
?subject
WHERE {
  ?dataset a sparc:Dataset .
  ?dataset TEMP:isAboutParticipant ?subject .
  ?subject a sparc:Subject .
}
LIMIT ?limit

Total

Show me the total number of subjects in the knowledge graph.

SELECT DISTINCT
(COUNT(DISTINCT ?subject) as ?count_subject)
WHERE {
  ?dataset a sparc:Dataset .
  ?dataset TEMP:isAboutParticipant ?subject .
  ?subject a sparc:Subject .
}

Subject Metadata

Show me a regularized set of metadata for all subjects that includes the following.

  • Identifier
  • Group
  • Species
  • Strain
  • Sex
  • Age Category
  • Age
  • Mass
SELECT DISTINCT
?local_id

?assigned_group

?l_species
?strain
?l_sex

?age_category
?age_value
?age_unit

?mass_value
?mass_unit

WHERE {
  ?subject a sparc:Subject .
  ?subject TEMP:localId ?local_id .
  ?subject sparc:animalSubjectIsOfSpecies ?species . OPTIONAL { ?species rdfs:label ?l_species . }
  OPTIONAL { ?subject sparc:animalSubjectIsOfStrain ?strain . } # ?strain rdfs:label ?l_strain .
  OPTIONAL { ?subject TEMP:hasBiologicalSex ?sex . ?sex rdfs:label ?l_sex . }
  OPTIONAL { ?subject TEMP:hasAgeCategory ?age_category . }
  OPTIONAL { ?subject TEMP:hasAssignedGroup ?assigned_group . }
  # OPTIONAL { ?subject TEMP:participantInPerformanceOf ?protocol . }

  OPTIONAL {
  # mass
  ?subject sparc:animalSubjectHasWeight ?bn_mass .
  ?bn_mass a sparc:Measurement .
  ?bn_mass TEMP:hasUnit ?mass_unit .
  ?bn_mass rdf:value ?mass_value .
}
  OPTIONAL {
  # age
  ?subject TEMP:hasAge ?bn_age .
  ?bn_age a sparc:Measurement .
  ?bn_age TEMP:hasUnit ?age_unit .
  ?bn_age rdf:value ?age_value .
}
#  VALUES ?l_s {?species ?sex}  # doesn't work, if it did it would duplicate rows
#  ?l_s rdfs:label ?label
} LIMIT ?limit

TODO By sex

SELECT DISTINCT
(COUNT(DISTINCT ?subject) as ?count_subject)
WHERE {
  ?dataset a sparc:Dataset .
  ?dataset TEMP:isAboutParticipant ?subject .
  ?subject a sparc:Subject .
}

Samples

Members

Show me all of the experimental samples that a dataset contains information about.

SELECT DISTINCT
?dataset
?sample
WHERE {
  ?dataset a sparc:Dataset .
  ?dataset TEMP:isAboutParticipant ?sample .
  ?sample a sparc:Sample .
}
LIMIT ?limit

Total

Show me the total number of samples in the knowledge graph.

SELECT DISTINCT
(COUNT(DISTINCT ?sample) as ?count_sample)
WHERE {
  ?dataset a sparc:Dataset .
  ?dataset TEMP:isAboutParticipant ?sample .
  ?sample a sparc:Sample .
}

Sample predicates

SELECT DISTINCT
?p
WHERE {
  ?sample a sparc:Sample .
  ?sample ?p ?o .
}

Sample Metadata

Show me a regularized set of metadata for all subjects that includes the following.

  • Identifier
  • Group
  • Anatomical entity
SELECT DISTINCT
?subject_lid

?local_id

?assigned_group

(str(?anat_ent_src) as ?aess)

WHERE {
  ?sample a sparc:Sample .
  ?sample TEMP:localId ?local_id .

  ?sample TEMP:wasDerivedFromSubject ?subject .
  ?subject TEMP:localId ?subject_lid .
  ?subject a sparc:Subject .

  OPTIONAL { ?sample TEMP:hasAssignedGroup ?assigned_group . }
  OPTIONAL { ?sample TEMPRAW:wasExtractedFromAnatomicalRegion ?anat_ent_src . }
  # OPTIONAL { ?sample TEMP:hasAge ?abe . }
  # OPTIONAL { ?sample TEMP:participantInPerformanceOf ?protocol . }
}
LIMIT ?limit

Anatomical entities

Dataset

  • Involves
    SELECT DISTINCT
    ?dataset
    ?protocol
    #?ae
    (str(?aes) as ?entity)
    WHERE {
      ?dataset a sparc:Dataset .
      ?dataset TEMP:hasProtocol ?protocol .
      ?protocol TEMPRAW:involvesAnatomicalRegion ?aes .
      # ?ae rdfs:label ?l_ae .
    }
    ORDER BY ASC(?entity)
    LIMIT ?limit
    
  • About
    SELECT DISTINCT
    ?dataset
    # ?ae
    (str(?l_ae) as ?entity)
    WHERE {
      ?dataset a sparc:Dataset .
      ?dataset isAbout: ?ae .  # TODO not 100% on the modelling here
      ?ae rdfs:label ?l_ae .
      ?ae rdfs:subClassOf* UBERON:0001062 .  # have to load uberon for this
    }
    ORDER BY ASC(?l_ae)
    LIMIT ?limit
    
  • Sample Source
    SELECT DISTINCT
    ?dataset
    (str(?ae) as ?aes)
    # ?slid
    WHERE {
      ?sample TEMP:hasDerivedInformationAsParticipant ?dataset .
      ?sample TEMPRAW:wasExtractedFromAnatomicalRegion ?ae .
      ?sample a sparc:Sample .
      # ?sample TEMP:localId ?slid .
      ?dataset a sparc:Dataset .
    }
    ORDER BY
    DESC(?ae)
    # ASC(?slid)
    LIMIT ?limit
    

Protocol

  • Simple
    SELECT (COUNT(*) as ?count) {
      SELECT DISTINCT
      ?protocol
      WHERE {
        ?protocol a sparc:Protocol .
        FILTER CONTAINS(str(?protocol), "protocols.io/api")
        #FILTER CONTAINS(str(?protocol), "protocols.io")
      }
    }
    
    SELECT DISTINCT
    ?protocol
    WHERE {
      ?protocol a sparc:Protocol .
      FILTER CONTAINS(str(?protocol), "protocols.io/api")
      #FILTER CONTAINS(str(?protocol), "protocols.io")
    }
    
  • Protocol triples
    SELECT DISTINCT
    ?protocol
    ?p
    ?o
    WHERE {
      ?protocol a sparc:Protocol .
      ?protocol ?p ?o
      FILTER CONTAINS(str(?protocol), "protocols.io")
    } LIMIT ?limit
    
  • Involves

    NOTE In order for these to work with subClassOf UBERON uberon must be loaded.

    SELECT DISTINCT
    ?protocol
    ?bb_value
    (str(?l_bb) as ?ls)
    WHERE {
      ?protocol a sparc:Protocol .
      ?protocol TEMP:protocolInvolvesBlackBox ?ast_bb .
    
      ?ast_bb rdf:type protcur:black-box .  # TODO need to refine on organ an ingest the new alignment
      ?ast_bb TEMP:hasValue ?bb_value .
    
      ?bb_value rdfs:subClassOf+ UBERON:0001062 .
      ?bb_value rdfs:label ?l_bb .
    } LIMIT ?limit
    
    SELECT DISTINCT
    ?protocol
    ?bbc_value
    (str(?l_bbc) as ?bbl)
    WHERE {
      ?protocol a sparc:Protocol .
      ?protocol TEMP:protocolInvolvesBlackBoxComponent ?ast_bbc .
    
      ?ast_bbc rdf:type protcur:black-box-component .  # TODO need to refine on organ an ingest the new alignment
      ?ast_bbc TEMP:hasValue ?bbc_value .
    
      ?bbc_value rdfs:subClassOf+ UBERON:0001062 .
      ?bbc_value rdfs:label ?l_bbc .
    } ORDER BY ?bbl LIMIT ?limit
    
  • Protc annotations
    grep -oE 'a\ protcur:[^\ ]+' protcur.ttl | sort -u | cut -d' ' -f2
    
    select (count(*) as ?count) {
      select distinct ?anno where {
        # TODO subPropertyOf
        #values ?anno_predicate {}  # this is a bad way to do this
        # ?s ?anno_predicate ?anno
        values ?anno_type {
          protcur:aspect
          protcur:black-box
          protcur:black-box-component
          protcur:executor-verb
          protcur:input
          protcur:invariant
          protcur:output
          protcur:parameter
        }
        ?anno a ?anno_type .
    }}
    
    select (count(*) as ?count) {
      select distinct ?anno where {
        # TODO subPropertyOf
        #values ?anno_predicate {}  # this is a bad way to do this
        # ?s ?anno_predicate ?anno
        values ?anno_type {
          protcur:aspect
          protcur:black-box
          protcur:black-box-component
          protcur:executor-verb
          protcur:input
          protcur:invariant
          protcur:output
          protcur:parameter
        }
        ?anno a ?anno_type .
        ?protocol a sparc:Protocol .
        ?protocol ?p ?banno .
        ?banno TEMP:protcurChildren* ?anno .
        FILTER CONTAINS(str(?protocol), "protocols.io/api")
        #FILTER CONTAINS(str(?protocol), "protocols.io")
      }
    }
    
    select distinct ?anno where {
      # TODO subPropertyOf
      #values ?anno_predicate {}  # this is a bad way to do this
      # ?s ?anno_predicate ?anno
      values ?anno_type {
        protcur:aspect
        protcur:black-box
        protcur:black-box-component
        protcur:executor-verb
        protcur:input
        protcur:invariant
        protcur:output
        protcur:parameter
      }
      ?anno a ?anno_type .
      ?protocol a sparc:Protocol .
      ?protocol ?p ?banno .
      ?banno TEMP:protcurChildren* ?anno .
      FILTER CONTAINS(str(?protocol), "protocols.io/api")
      #FILTER CONTAINS(str(?protocol), "protocols.io")
    }
    
    select distinct ?anno where {
      # TODO subPropertyOf
      #values ?anno_predicate {}  # this is a bad way to do this
      # ?s ?anno_predicate ?anno
      values ?anno_type {
        protcur:aspect
        protcur:black-box
        protcur:black-box-component
        protcur:executor-verb
        protcur:input
        protcur:invariant
        protcur:output
        protcur:parameter
      }
      ?anno a ?anno_type .
    

Uberon tests

In order to use these you need to load the latest version of uberon into blazegraph.

SELECT DISTINCT ?sc (str(?l) as ?label) WHERE {
  # BIND("brain" AS ?label) # Well would you look at that!
  VALUES (?root) { ( UBERON:0001062 ) }
  ?sc rdfs:subClassOf+ ?root .
  ?sc rdfs:label ?l .
} ORDER BY ASC(?label) LIMIT ?limit
select ?p ?o where {
    VALUES (?s) { ( UBERON:0001062 ) }
    ?s ?p ?o .
}

Associated scaffolds

Manifest files

A SODA development use case. Find me datasets that have top level manifests with no errors.

SELECT DISTINCT
?dataset
WHERE {
  ?dataset a sparc:Dataset .
  ?dataset TEMP:hasPart+ ?file .
  ?file a sparc:File .
  ?file a sparc:Manifest .
  ?file TEMP:hasParent ?dataset . # this is the tricky bit, because we conflate the identifier for dataset and the folder
  ?file TEMP:hasErrors 0 .
}
LIMIT ?limit

Contributors

Count

SELECT DISTINCT
(COUNT(DISTINCT ?person) as ?cp)
WHERE {
  ?person a sparc:Person .
}
LIMIT ?limit

ORCiD

Count the number of contributors that have ORCiDs.

SELECT DISTINCT
(COUNT(DISTINCT ?person) as ?cp)
#?fn
#?ln
WHERE {
  ?person a sparc:Person .
  ?person sparc:firstName ?fn .
  ?person sparc:lastName ?ln .
  FILTER CONTAINS(str(?person), "orcid.org")
}
LIMIT ?limit

Other

NOTE These are not yet in the ttl file, the queries written here will not work yet.

File types

In all likelihood we are not going to include the names of each of the individual files in the standard ttl export. We may put it in a named graph and then update the journal, possibly only for released datasets. The use cases for having individual files in the graph is not at all clear, we might have individual folders, or run it in the other direction where a subject could list the folders that contain data about it. Trying to keep the graph in sync with Blackfynn would be quite a pain, essentially we would store/append every single file that ever appears and then mark the deleted ones as deleted or something.

SELECT DISTINCT
?dataset
?file_type
WHERE {
  ?dataset a sparc:Dataset .
  ?dataset TEMP:containsFileWithType ?file_type .  # TODO not 100% on the modelling here
}
LIMIT ?limit

In the path-metadata folder of curation-export.

grep -rh 'mimetype":' | sort | uniq -c | sort -h

This doesn't get coverage for file extensions so in addition we run the following.

grep -rh 'basename":.\+\.\([a-z]\+\)' | grep -o '\.[a-z]\+"' | sort | uniq -c | sort -h

Has Raw Data

SELECT DISTINCT
?dataset
?raw
WHERE {
  ?dataset a sparc:Dataset .
  ?dataset TEMP:containsFolderForRawData ?raw .  # TODO not 100% on the modelling here
}
LIMIT ?limit

Has Derived Data

SELECT DISTINCT
?dataset
?derived
WHERE {
  ?dataset a sparc:Dataset .
  ?dataset TEMP:containsFolderForDerivedData ?derived .  # TODO not 100% on the modelling here
}
LIMIT ?limit

Has Code

SELECT DISTINCT
?dataset
?code
WHERE {
  ?dataset a sparc:Dataset .
  ?dataset TEMP:containsFolderForCode ?code .  # TODO not 100% on the modelling here
}
LIMIT ?limit

Used in simulation

This modeling is extremely preliminary.

SELECT DISTINCT
?dataset
?dataset_sim
WHERE {
  ?dataset a sparc:Dataset .
  ?dataset_sim a sparc:Dataset .  # TODO not 100% on this
  # the dataset holds the simulation, and is also what references the other datasets
  # whether we need an explicit type for simulation datasets is not clear, I suspect
  # that we do not, since datasets are just data, the aboutness or typeness probably
  # should come from the fact that the dataset specifies or houses a simulation ...
  # ?dataset_sim a sparc:SimulationDataset . # also not good
  # ?dataset_sim TEMP:isSubstrateForSomeComputationalSimulation true .  # FIXME this is bad bad bad
  VALUES ?p {TEMP:derivesParametersFrom TEMP:derivesValidationDataFrom ilxtr:hasInformationInput} .
  ?dataset_sim ?p ?dataset .
}
LIMIT ?limit

Subject reconciliation

first pass requires that the subject_id be identical

v2

SELECT (COUNT(*) as ?count) {
  <<pds-v2>>
}
SELECT DISTINCT ?s ?dataset (str(?id) as ?local_id) ?award ?species
WHERE {

  ?s a sparc:Subject;
     TEMP:localId ?id;
     TEMP:hasDerivedInformationAsParticipant ?dataset ;
     sparc:animalSubjectIsOfSpecies ?species .
  ?dataset a sparc:Dataset ;
           TEMP:hasAwardNumber ?award .

 { SELECT DISTINCT ?id
    WHERE {
          ?s a sparc:Subject ;
              TEMP:localId ?id ;
              TEMP:hasDerivedInformationAsParticipant ?dataset .
    } GROUP BY ?id HAVING (count(?id) > 1) }

}
ORDER BY ?id

v1

# :results table
# RUN:block
SELECT distinct ?s1 ?s2 ?id ?dataset1 ?dataset2 ?species1 ?species2 ?award1 ?award2
WHERE {

  # Give me all subjects with duplicate IDs with unique datasets
  { SELECT ?id
    WHERE {
          ?s1 a owl:NamedIndividual ;
          TEMP:localId ?id ;
          TEMP:hasDerivedInformationAsParticipant ?datasetx .
          ?s2 a owl:NamedIndividual ;
          TEMP:localId ?id ;
          TEMP:hasDerivedInformationAsParticipant ?datasety .
          filter(?datasetx != ?datasety)
    } GROUP BY ?id having (count(?id) > 1)}

  # filter out the cases where there can't be a match because species doesn't match
  ?s1 TEMP:localId ?id ;
  sparc:animalSubjectIsOfSpecies ?species1;
  TEMP:hasDerivedInformationAsParticipant ?dataset1 .
  ?s2 TEMP:localId ?id ;
  sparc:animalSubjectIsOfSpecies ?species2;
  TEMP:hasDerivedInformationAsParticipant ?dataset2 .
  filter(?s1 != ?s2)
  filter(STR(IRI(?s1)) < STR(IRI(?s2))) # this is a hack to filter out duplicate triples

  # filter to find non-matching awards
  # using contact person doesn't make sense as they are 1:1 with award right now
  ?dataset1 a owl:NamedIndividual ;
  TEMP:hasAwardNumber ?award1 .
  ?dataset2 a owl:NamedIndividual ;
  TEMP:hasAwardNumber ?award2 .
} order by ?id limit ?limit

Python helper

call

simple

Python code to simplify neru and supporting functions to extract terminal regions.

from pyontutils.config import auth
from nifstd_tools.simplify import apinat_deblob, sub, pred, obj, ematch
from nifstd_tools.simplify import axon, dend, bag, top, ie, ies, iot, iots, ext, fasIn, endIn, layerIn, onts

ext = onts  # XXX schema change


def query(neupop_id):
    import requests
    url = ('https://scicrunch.org/api/1/sparc-scigraph/scigraph/dynamic/'
           f'demos/apinat/neru-7/{neupop_id}?limit=9999999&key={auth.get("scigraph-api-key")}')
    resp = requests.get(url, headers={'Accept': 'application/json'})
    blob = resp.json()
    return blob


def find_terminals(blob, type):
    return [es for es in blob['edges']
            if
            pred(es, iots) and
            obj(es, type) and
            ematch(blob,
                   (lambda e, m:
                    sub(e, m) and
                    pred(e, top) and
                    obj(e, bag)),
                   sub(es))]


def find_region(blob, edge):
    collect = []
    def select(e, m, collect=collect):
        if sub(e, m):
            #print(e)
            if pred(e, layerIn) or pred(e, fasIn) or pred(e, endIn):
                return ematch(blob, select, obj(e))
            elif pred(e, onts):
                region = obj(e)
                collect.extend([b for b in blob['nodes'] if b['id'] == region])
                return region

    ematch(blob, select, sub(edge))
    return collect


def find_region_layer(blob, edge, bindex):
    collect = []
    layers = []
    layers_iots = []
    donel = set()
    doner = set()
    def select_term(e, m, layers=layers):
        if sub(e, m):
            pprint(e)
            if (pred(e, onts)
                or pred(e, iots)):
                layer = obj(e)
                if layer not in donel:
                    donel.add(layer)
                    if pred(e, iots):
                        layers_iots.append(bindex[layer])
                    else:
                        layers.append(bindex[layer])
                return layer

    def select(e, m, collect=collect):
        if sub(e, m):
            if pred(e, layerIn):
                # we're at a layer
                ematch(blob, select_term, sub(e))
                return ematch(blob, select, obj(e))
            elif (pred(e, fasIn)
                  or pred(e, endIn)):
                return ematch(blob, select, obj(e))
            elif pred(e, onts):
                region = obj(e)
                if region not in doner:
                    doner.add(region)
                    collect.append(bindex[region])
                return region
    ematch(blob, select, sub(edge))
    if collect and not layers and not layers_iots:
        layers = [None]
    elif layers_iots and not layers:
        layers = layers_iots

    if len(collect) != len(layers):
        raise ValueError(f'len not matched {len(collect)} {len(layers)}')
    return list(zip(collect, layers))


def find_terminal_regions(blob, type):
    return [region for es in find_terminals(blob, type) for region in find_region(blob, es)]


def find_terminal_region_layers(blob, type, bindex):
    return [(region, layer) for es in find_terminals(blob, type)
            for region, layer in find_region_layer(blob, es, bindex)]


def main(blob=None, blob_string=None, neupop_id=None, remove_converge=remove):
    # do not call this function unless it is from an org babel block, there are free variables
    if blob is None and blob_string is not None:
        import json
        blob = json.loads(blob_string)

    neupop_id = neupop_id if neupop_id else 'ilxtr:neuron-type-keast-6'

    if blob is None:
        blob = query(neupop_id)

    blob, edges, somas, externals, ordering_edges = v = apinat_deblob(
        blob, remove_converge=remove_converge)

    test_call = False  # change to True to test the #+call: expression
    if test_call:
        pprint(v)
    return v
pprint(blob)

terminal regions

Find the ontology identifiers for terminal regions.

from pyontutils.config import auth
from nifstd_tools.simplify import apinat_deblob, sub, pred, obj, ematch
from nifstd_tools.simplify import axon, dend, bag, top, ie, ies, iot, iots, ext, fasIn, endIn, layerIn, onts

ext = onts  # XXX schema change


def query(neupop_id):
    import requests
    url = ('https://scicrunch.org/api/1/sparc-scigraph/scigraph/dynamic/'
           f'demos/apinat/neru-7/{neupop_id}?limit=9999999&key={auth.get("scigraph-api-key")}')
    resp = requests.get(url, headers={'Accept': 'application/json'})
    blob = resp.json()
    return blob


def find_terminals(blob, type):
    return [es for es in blob['edges']
            if
            pred(es, iots) and
            obj(es, type) and
            ematch(blob,
                   (lambda e, m:
                    sub(e, m) and
                    pred(e, top) and
                    obj(e, bag)),
                   sub(es))]


def find_region(blob, edge):
    collect = []
    def select(e, m, collect=collect):
        if sub(e, m):
            #print(e)
            if pred(e, layerIn) or pred(e, fasIn) or pred(e, endIn):
                return ematch(blob, select, obj(e))
            elif pred(e, onts):
                region = obj(e)
                collect.extend([b for b in blob['nodes'] if b['id'] == region])
                return region

    ematch(blob, select, sub(edge))
    return collect


def find_region_layer(blob, edge, bindex):
    collect = []
    layers = []
    layers_iots = []
    donel = set()
    doner = set()
    def select_term(e, m, layers=layers):
        if sub(e, m):
            pprint(e)
            if (pred(e, onts)
                or pred(e, iots)):
                layer = obj(e)
                if layer not in donel:
                    donel.add(layer)
                    if pred(e, iots):
                        layers_iots.append(bindex[layer])
                    else:
                        layers.append(bindex[layer])
                return layer

    def select(e, m, collect=collect):
        if sub(e, m):
            if pred(e, layerIn):
                # we're at a layer
                ematch(blob, select_term, sub(e))
                return ematch(blob, select, obj(e))
            elif (pred(e, fasIn)
                  or pred(e, endIn)):
                return ematch(blob, select, obj(e))
            elif pred(e, onts):
                region = obj(e)
                if region not in doner:
                    doner.add(region)
                    collect.append(bindex[region])
                return region
    ematch(blob, select, sub(edge))
    if collect and not layers and not layers_iots:
        layers = [None]
    elif layers_iots and not layers:
        layers = layers_iots

    if len(collect) != len(layers):
        raise ValueError(f'len not matched {len(collect)} {len(layers)}')
    return list(zip(collect, layers))


def find_terminal_regions(blob, type):
    return [region for es in find_terminals(blob, type) for region in find_region(blob, es)]


def find_terminal_region_layers(blob, type, bindex):
    return [(region, layer) for es in find_terminals(blob, type)
            for region, layer in find_region_layer(blob, es, bindex)]


def main(blob=None, blob_string=None, neupop_id=None, remove_converge=remove):
    # do not call this function unless it is from an org babel block, there are free variables
    if blob is None and blob_string is not None:
        import json
        blob = json.loads(blob_string)

    neupop_id = neupop_id if neupop_id else 'ilxtr:neuron-type-keast-6'

    if blob is None:
        blob = query(neupop_id)

    blob, edges, somas, externals, ordering_edges = v = apinat_deblob(
        blob, remove_converge=remove_converge)

    test_call = False  # change to True to test the #+call: expression
    if test_call:
        pprint(v)
    return v
from pprint import pprint

blob, *_ = main(blob_string=result)
bindex = {n['id']:n for n in blob['nodes']}

# find terminal regions
axon_terminal_regions = find_terminal_regions(blob, axon)
dend_terminal_regions = find_terminal_regions(blob, dend)
#pprint(axon_terminal_regions)
#pprint(dend_terminal_regions)

axon_terminal_rl = find_terminal_region_layers(blob, axon, bindex)
dend_terminal_rl = find_terminal_region_layers(blob, dend, bindex)
pprint([(r['id'], (l if l is None else l['id'])) for r, l in axon_terminal_rl])
pprint([(r['id'], (l if l is None else l['id'])) for r, l in dend_terminal_rl])

connectivity pairs

This code produces the basic connectivity of a neuron population. The result is a list of pairs, the pairs are the subject and object of the triple subject (or apinatomy:next apinatomy:next*) object. Thus they represent the full connectivity of the population.

The contents of each element of the pair tuples with three elements.

  1. The id for the neuronal process involved in the connection.
  2. The ontology id for the layer in which the neuronal process is located. This may be None if no layer is specified in the model.
  3. The ontology id for the region in which the neuronal process is located.

In the example presented here for keast neuron 5 the final entries all have the same start and end location because they are the soma, and the roots of the axon and the dendrite.

intIn = 'apinatomy:internalIn'

def isLayer(blob, s):
    return ematch(blob, (lambda e, m: sub(e, m) and pred(e, layerIn)), s)

def reclr(blob, start_link):
    # recurse up the hierarchy until fasIn endIn intIn terminates
    #print('starting reclr')
    collect = []
    layer = []
    col = True
    def select_ext(e, m, collect=collect):
        nonlocal col
        nonlocal layer
        if sub(e, m):
            if pred(e, 'apinatomy:cloneOf'):  # should be zapped during simplify
                return ematch(blob, select_ext, obj(e))
            if pred(e, onts) or pred(e, iot) or pred(e, iots):
                #print('select_ext', e)
                external = obj(e)
                #print('se', col, layer)
                if col:
                    if layer:
                        if len(layer) > 1:
                            l, *layer = layer
                        else:
                            l = layer.pop()
                    else:
                        l = None
                    r = [b for b in blob['nodes'] if b['id'] == external][0]['id']  # if this is empty we are in big trouble
                    collect.append((l, r))
                else:
                    l = [b for b in blob['nodes'] if b['id'] == external][0]['id']
                    layer.append(l)
                #print(layer)
                return external

    def select(e, m):
        nonlocal col
        if sub(e, m):
            #print(e)
            if pred(e, layerIn) or pred(e, fasIn) or pred(e, endIn) or pred(e, intIn):
                #print('select', e)
                col = not isLayer(blob, obj(e))
                #print('s', col)
                ematch(blob, select_ext, obj(e))
                ematch(blob, select, obj(e))

    ematch(blob, select, start_link)

    return collect

def lay_reg(blob, start):
    direct = [obj(t) for t in
              ematch(blob, (lambda e, m: sub(e, m)
                            and (pred(e, intIn) or  # XXX why isn't this getting pulled in correctly for somas? answer: missing somas in starts? surely not, ARGH a lyph internalIn a layer
                                 pred(e, endIn) or
                                 pred(e, fasIn))),
                     start)]

    layers = [obj(t) for d in direct for t in
              ematch(blob, (lambda e, m: sub(e, m)
                            and isLayer(blob, m)
                            and (pred(e, iot) or
                                 pred(e, iots) or
                                 pred(e, onts))),
                     d)]

    lregs = []
    if layers:
        ldir = [obj(t) for d in direct for t in
                ematch(blob, (lambda e, m: sub(e, m)
                              and pred(e, layerIn)),
                       d)]

        lregs = [obj(t) for d in ldir for t in
                 ematch(blob, (lambda e, m: sub(e, m)
                               and not isLayer(blob, m)
                               and (pred(e, iot) or
                                    pred(e, iots) or  # XXX ie seems to have no hits now
                                    pred(e, onts))),
                        d)]

    regions = [obj(t) for d in direct for t in
               ematch(blob, (lambda e, m: sub(e, m)
                             and not isLayer(blob, m)
                             and (pred(e, iot) or
                                  pred(e, iots) or
                                  pred(e, onts))),
                      d)]

    #if start == 'kblad:snl16':
        #print(start, direct, layers, regions)

    # FIXME TODO nested regions and layers
    lrs = reclr(blob, start)  # FIXME doesn't pair regions and layers
    # FIXME SIGH SIGH SIGH start can be either part of a next >_<
    #print(lrs)

    assert not (lregs and regions), (lregs, regions)  # not both
    regions = lregs if lregs else regions
    #out = start, layers[0] if layers else None, regions[0] if regions else None, tuple([l for l in lrs])
    out = start, tuple(lrs)
    if out:
      return out

def render_blob(blob):
    starts = [obj(e) for e in blob['edges'] if pred(e, 'apinatomy:lyphs')]

    nxt = 'apinatomy:next'
    nxts = 'apinatomy:next*'

    nexts = [(sub(t), obj(t)) for start in starts for t in
             ematch(blob, (lambda e, m: pred(e, nxt) or pred(e, nxts)), None)]

    #pprint(nexts)

    connected_pairs = sorted(set([tuple([lay_reg(blob, e) for e in p]) for p in nexts]))
    #pprint(connected_pairs)
    return connected_pairs
blob, *_ = main(blob_string=result)
render_blob(blob)

neru

from pprint import pprint
from pyontutils.scigraph import Cypher, Vocabulary
from pyontutils.scigraph_codegen import moduleDirect
from pyontutils.config import auth
scigraphd = moduleDirect(auth.get('scigraph-api'), 'scigraphd')
from pyontutils.config import auth
from nifstd_tools.simplify import apinat_deblob, sub, pred, obj, ematch
from nifstd_tools.simplify import axon, dend, bag, top, ie, ies, iot, iots, ext, fasIn, endIn, layerIn, onts

ext = onts  # XXX schema change


def query(neupop_id):
    import requests
    url = ('https://scicrunch.org/api/1/sparc-scigraph/scigraph/dynamic/'
           f'demos/apinat/neru-7/{neupop_id}?limit=9999999&key={auth.get("scigraph-api-key")}')
    resp = requests.get(url, headers={'Accept': 'application/json'})
    blob = resp.json()
    return blob


def find_terminals(blob, type):
    return [es for es in blob['edges']
            if
            pred(es, iots) and
            obj(es, type) and
            ematch(blob,
                   (lambda e, m:
                    sub(e, m) and
                    pred(e, top) and
                    obj(e, bag)),
                   sub(es))]


def find_region(blob, edge):
    collect = []
    def select(e, m, collect=collect):
        if sub(e, m):
            #print(e)
            if pred(e, layerIn) or pred(e, fasIn) or pred(e, endIn):
                return ematch(blob, select, obj(e))
            elif pred(e, onts):
                region = obj(e)
                collect.extend([b for b in blob['nodes'] if b['id'] == region])
                return region

    ematch(blob, select, sub(edge))
    return collect


def find_region_layer(blob, edge, bindex):
    collect = []
    layers = []
    layers_iots = []
    donel = set()
    doner = set()
    def select_term(e, m, layers=layers):
        if sub(e, m):
            pprint(e)
            if (pred(e, onts)
                or pred(e, iots)):
                layer = obj(e)
                if layer not in donel:
                    donel.add(layer)
                    if pred(e, iots):
                        layers_iots.append(bindex[layer])
                    else:
                        layers.append(bindex[layer])
                return layer

    def select(e, m, collect=collect):
        if sub(e, m):
            if pred(e, layerIn):
                # we're at a layer
                ematch(blob, select_term, sub(e))
                return ematch(blob, select, obj(e))
            elif (pred(e, fasIn)
                  or pred(e, endIn)):
                return ematch(blob, select, obj(e))
            elif pred(e, onts):
                region = obj(e)
                if region not in doner:
                    doner.add(region)
                    collect.append(bindex[region])
                return region
    ematch(blob, select, sub(edge))
    if collect and not layers and not layers_iots:
        layers = [None]
    elif layers_iots and not layers:
        layers = layers_iots

    if len(collect) != len(layers):
        raise ValueError(f'len not matched {len(collect)} {len(layers)}')
    return list(zip(collect, layers))


def find_terminal_regions(blob, type):
    return [region for es in find_terminals(blob, type) for region in find_region(blob, es)]


def find_terminal_region_layers(blob, type, bindex):
    return [(region, layer) for es in find_terminals(blob, type)
            for region, layer in find_region_layer(blob, es, bindex)]


def main(blob=None, blob_string=None, neupop_id=None, remove_converge=remove):
    # do not call this function unless it is from an org babel block, there are free variables
    if blob is None and blob_string is not None:
        import json
        blob = json.loads(blob_string)

    neupop_id = neupop_id if neupop_id else 'ilxtr:neuron-type-keast-6'

    if blob is None:
        blob = query(neupop_id)

    blob, edges, somas, externals, ordering_edges = v = apinat_deblob(
        blob, remove_converge=remove_converge)

    test_call = False  # change to True to test the #+call: expression
    if test_call:
        pprint(v)
    return v
intIn = 'apinatomy:internalIn'

def isLayer(blob, s):
    return ematch(blob, (lambda e, m: sub(e, m) and pred(e, layerIn)), s)

def reclr(blob, start_link):
    # recurse up the hierarchy until fasIn endIn intIn terminates
    #print('starting reclr')
    collect = []
    layer = []
    col = True
    def select_ext(e, m, collect=collect):
        nonlocal col
        nonlocal layer
        if sub(e, m):
            if pred(e, 'apinatomy:cloneOf'):  # should be zapped during simplify
                return ematch(blob, select_ext, obj(e))
            if pred(e, onts) or pred(e, iot) or pred(e, iots):
                #print('select_ext', e)
                external = obj(e)
                #print('se', col, layer)
                if col:
                    if layer:
                        if len(layer) > 1:
                            l, *layer = layer
                        else:
                            l = layer.pop()
                    else:
                        l = None
                    r = [b for b in blob['nodes'] if b['id'] == external][0]['id']  # if this is empty we are in big trouble
                    collect.append((l, r))
                else:
                    l = [b for b in blob['nodes'] if b['id'] == external][0]['id']
                    layer.append(l)
                #print(layer)
                return external

    def select(e, m):
        nonlocal col
        if sub(e, m):
            #print(e)
            if pred(e, layerIn) or pred(e, fasIn) or pred(e, endIn) or pred(e, intIn):
                #print('select', e)
                col = not isLayer(blob, obj(e))
                #print('s', col)
                ematch(blob, select_ext, obj(e))
                ematch(blob, select, obj(e))

    ematch(blob, select, start_link)

    return collect

def lay_reg(blob, start):
    direct = [obj(t) for t in
              ematch(blob, (lambda e, m: sub(e, m)
                            and (pred(e, intIn) or  # XXX why isn't this getting pulled in correctly for somas? answer: missing somas in starts? surely not, ARGH a lyph internalIn a layer
                                 pred(e, endIn) or
                                 pred(e, fasIn))),
                     start)]

    layers = [obj(t) for d in direct for t in
              ematch(blob, (lambda e, m: sub(e, m)
                            and isLayer(blob, m)
                            and (pred(e, iot) or
                                 pred(e, iots) or
                                 pred(e, onts))),
                     d)]

    lregs = []
    if layers:
        ldir = [obj(t) for d in direct for t in
                ematch(blob, (lambda e, m: sub(e, m)
                              and pred(e, layerIn)),
                       d)]

        lregs = [obj(t) for d in ldir for t in
                 ematch(blob, (lambda e, m: sub(e, m)
                               and not isLayer(blob, m)
                               and (pred(e, iot) or
                                    pred(e, iots) or  # XXX ie seems to have no hits now
                                    pred(e, onts))),
                        d)]

    regions = [obj(t) for d in direct for t in
               ematch(blob, (lambda e, m: sub(e, m)
                             and not isLayer(blob, m)
                             and (pred(e, iot) or
                                  pred(e, iots) or
                                  pred(e, onts))),
                      d)]

    #if start == 'kblad:snl16':
        #print(start, direct, layers, regions)

    # FIXME TODO nested regions and layers
    lrs = reclr(blob, start)  # FIXME doesn't pair regions and layers
    # FIXME SIGH SIGH SIGH start can be either part of a next >_<
    #print(lrs)

    assert not (lregs and regions), (lregs, regions)  # not both
    regions = lregs if lregs else regions
    #out = start, layers[0] if layers else None, regions[0] if regions else None, tuple([l for l in lrs])
    out = start, tuple(lrs)
    if out:
      return out

def render_blob(blob):
    starts = [obj(e) for e in blob['edges'] if pred(e, 'apinatomy:lyphs')]

    nxt = 'apinatomy:next'
    nxts = 'apinatomy:next*'

    nexts = [(sub(t), obj(t)) for start in starts for t in
             ematch(blob, (lambda e, m: pred(e, nxt) or pred(e, nxts)), None)]

    #pprint(nexts)

    connected_pairs = sorted(set([tuple([lay_reg(blob, e) for e in p]) for p in nexts]))
    #pprint(connected_pairs)
    return connected_pairs
sgv = Vocabulary()
sgd = scigraphd.Dynamic(cache=True, verbose=True, do_error=True)
sgc = scigraphd.Cypher(cache=True, verbose=True)
q_neru_6 = """
MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK

, (neugrp)
-[:apinatomy:links]->(link)
-[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
-[d:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph

WITH neugrp, link, lyph, a, c, d
OPTIONAL MATCH layer_ext = (lyph)
<-[d*1]-(layer)-[:apinatomy:cloneOf*0..1]->()-[:apinatomy:inheritedExternal]->()

WITH neugrp, link, lyph, a, c, d, layer_ext
OPTIONAL MATCH more = (lyph)
-[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
-[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()

WITH neugrp, link, lyph, a, c, d, layer_ext, more // there is a difference here because the previous match does not require lyphs to have external ids
MATCH (lyph)
-[e:apinatomy:ontologyTerms]->(region)

, p2 = (link)
-[:apinatomy:conveyingLyph]->(cl)
-[:apinatomy:topology]->()

, (cl)
-[x:apinatomy:inheritedExternal*0..1]->()

// use apinatomy:next to extract ordering information
, (link)
-[f:apinatomy:next|apinatomy:nextChainStartLevels*0..]->()
-[g:apinatomy:target*0..1]->()
-[h:apinatomy:rootOf*0..1]->()
-[i:apinatomy:levels*0..1]->()
<-[:apinatomy:links]-(neugrp)

// publications
WITH neugrp, a, c, d, e, f, g,h,i, p2, x, layer_ext, more
OPTIONAL MATCH path = (neugrp)
-[:apinatomy:references]->(pub)
-[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Reference"}) // cannot be curied, dynamic endpoints will not expand it

RETURN a, null as b, c, d, e, f, g,h,i, path, p2, x, layer_ext, more

UNION

// this part usually only returns the soma housing lyph
MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
-[b:apinatomy:lyphs]->(lyph)
-[c:apinatomy:internalIn]->()
-[d:apinatomy:ontologyTerms*0..1]->(region)
, p2 = (lyph)
-[:apinatomy:conveys]->(soma_link)
-[:apinatomy:source|apinatomy:target]->(soma_node)
-[:apinatomy:sourceOf]->(chain_link)
, (chain_link)
-[:apinatomy:levelIn]->(chain)
, (soma_node)
-[:apinatomy:rootOf]->(chain)

WITH lyph, a, b, c, d, p2
OPTIONAL MATCH more = (lyph)
-[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
-[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()

RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path, p2, null as x, null as layer_ext, more
"""
def neupop_blob(neupop_id):
    # XXX requires a patch to Cypher and CypherBase to support arbitrary kwargs
    blob = sgc.execute(query=q_neru_6, limit=None, neupop_id=neupop_id, output='application/json')
    return blob

def sigh():
    blobs = [neupop_blob(f"ilxtr:neuron-type-keast-{npid}") for npid in range(1, 21)]
    debs = [main(blob=blob)[0] for blob in blobs]
    simples = [render_blob(deb) for deb in debs]
    #[pprint(simple) for simple in simples[:1]]

def fbid(id):
    if id:
        result = sgv.findById(id)
        if result['labels']:
            return result['labels'][0]

def fbids(ids):
    return [(id, fbid(id)) for id in ids]

def rendlab(pairs):
    for pair in pairs:
        yield tuple((start, *(tuple(fbids(hp)) for hp in housings)) for start, housings in pair)
    #for (*tstart, sid, sasdf), (*tend, eid, easdf) in pairs:
        #yield ((*tstart, sid, fbid(sid), fbids(sasdf)) if sid else (*tstart, sid, fbids(sasdf)),
               #(*tend, eid, fbid(eid), fbids(easdf)) if eid else (*tend, eid, fbids(easdf)))
from copy import deepcopy

blobs = [neupop_blob(f"ilxtr:neuron-type-keast-{npid}") for npid in range(1, 21)]

debs = [main(blob=deepcopy(blob))[0] for blob in blobs]

simples = [render_blob(deb) for deb in debs]

sigh = [s for n, si in enumerate(simples)  for s in ((f'--------- {n + 1}','---------'), *sorted(rendlab(si)))]

#sigh
#pprint(blobs[0])
#pprint(debs[0])
#pprint(simples[0])
#sigh[:200]
sigh

asdf

select distinct ?s ?p where {
  ?s ?p ilxtr:NeuronCUT . }
// MATCH path = (a)-[t]-(:Class) RETURN a
//MATCH path = ()-[:apinatomy:conveyingLyph]-() RETURN path
// MATCH path = ()-[:TEMPRAW:protocolInvolvesInputInstance]-() RETURN path

heatmaps

TODO get the numbers along the axis, so n populations

TODO color code by process type

TODO costimulation by location

neupop by region

from pprint import pprint
from pyontutils.scigraph import Cypher, Vocabulary
from pyontutils.scigraph_codegen import moduleDirect
from pyontutils.config import auth
scigraphd = moduleDirect(auth.get('scigraph-api'), 'scigraphd')
from pyontutils.config import auth
from nifstd_tools.simplify import apinat_deblob, sub, pred, obj, ematch
from nifstd_tools.simplify import axon, dend, bag, top, ie, ies, iot, iots, ext, fasIn, endIn, layerIn, onts

ext = onts  # XXX schema change


def query(neupop_id):
    import requests
    url = ('https://scicrunch.org/api/1/sparc-scigraph/scigraph/dynamic/'
           f'demos/apinat/neru-7/{neupop_id}?limit=9999999&key={auth.get("scigraph-api-key")}')
    resp = requests.get(url, headers={'Accept': 'application/json'})
    blob = resp.json()
    return blob


def find_terminals(blob, type):
    return [es for es in blob['edges']
            if
            pred(es, iots) and
            obj(es, type) and
            ematch(blob,
                   (lambda e, m:
                    sub(e, m) and
                    pred(e, top) and
                    obj(e, bag)),
                   sub(es))]


def find_region(blob, edge):
    collect = []
    def select(e, m, collect=collect):
        if sub(e, m):
            #print(e)
            if pred(e, layerIn) or pred(e, fasIn) or pred(e, endIn):
                return ematch(blob, select, obj(e))
            elif pred(e, onts):
                region = obj(e)
                collect.extend([b for b in blob['nodes'] if b['id'] == region])
                return region

    ematch(blob, select, sub(edge))
    return collect


def find_region_layer(blob, edge, bindex):
    collect = []
    layers = []
    layers_iots = []
    donel = set()
    doner = set()
    def select_term(e, m, layers=layers):
        if sub(e, m):
            pprint(e)
            if (pred(e, onts)
                or pred(e, iots)):
                layer = obj(e)
                if layer not in donel:
                    donel.add(layer)
                    if pred(e, iots):
                        layers_iots.append(bindex[layer])
                    else:
                        layers.append(bindex[layer])
                return layer

    def select(e, m, collect=collect):
        if sub(e, m):
            if pred(e, layerIn):
                # we're at a layer
                ematch(blob, select_term, sub(e))
                return ematch(blob, select, obj(e))
            elif (pred(e, fasIn)
                  or pred(e, endIn)):
                return ematch(blob, select, obj(e))
            elif pred(e, onts):
                region = obj(e)
                if region not in doner:
                    doner.add(region)
                    collect.append(bindex[region])
                return region
    ematch(blob, select, sub(edge))
    if collect and not layers and not layers_iots:
        layers = [None]
    elif layers_iots and not layers:
        layers = layers_iots

    if len(collect) != len(layers):
        raise ValueError(f'len not matched {len(collect)} {len(layers)}')
    return list(zip(collect, layers))


def find_terminal_regions(blob, type):
    return [region for es in find_terminals(blob, type) for region in find_region(blob, es)]


def find_terminal_region_layers(blob, type, bindex):
    return [(region, layer) for es in find_terminals(blob, type)
            for region, layer in find_region_layer(blob, es, bindex)]


def main(blob=None, blob_string=None, neupop_id=None, remove_converge=remove):
    # do not call this function unless it is from an org babel block, there are free variables
    if blob is None and blob_string is not None:
        import json
        blob = json.loads(blob_string)

    neupop_id = neupop_id if neupop_id else 'ilxtr:neuron-type-keast-6'

    if blob is None:
        blob = query(neupop_id)

    blob, edges, somas, externals, ordering_edges = v = apinat_deblob(
        blob, remove_converge=remove_converge)

    test_call = False  # change to True to test the #+call: expression
    if test_call:
        pprint(v)
    return v
intIn = 'apinatomy:internalIn'

def isLayer(blob, s):
    return ematch(blob, (lambda e, m: sub(e, m) and pred(e, layerIn)), s)

def reclr(blob, start_link):
    # recurse up the hierarchy until fasIn endIn intIn terminates
    #print('starting reclr')
    collect = []
    layer = []
    col = True
    def select_ext(e, m, collect=collect):
        nonlocal col
        nonlocal layer
        if sub(e, m):
            if pred(e, 'apinatomy:cloneOf'):  # should be zapped during simplify
                return ematch(blob, select_ext, obj(e))
            if pred(e, onts) or pred(e, iot) or pred(e, iots):
                #print('select_ext', e)
                external = obj(e)
                #print('se', col, layer)
                if col:
                    if layer:
                        if len(layer) > 1:
                            l, *layer = layer
                        else:
                            l = layer.pop()
                    else:
                        l = None
                    r = [b for b in blob['nodes'] if b['id'] == external][0]['id']  # if this is empty we are in big trouble
                    collect.append((l, r))
                else:
                    l = [b for b in blob['nodes'] if b['id'] == external][0]['id']
                    layer.append(l)
                #print(layer)
                return external

    def select(e, m):
        nonlocal col
        if sub(e, m):
            #print(e)
            if pred(e, layerIn) or pred(e, fasIn) or pred(e, endIn) or pred(e, intIn):
                #print('select', e)
                col = not isLayer(blob, obj(e))
                #print('s', col)
                ematch(blob, select_ext, obj(e))
                ematch(blob, select, obj(e))

    ematch(blob, select, start_link)

    return collect

def lay_reg(blob, start):
    direct = [obj(t) for t in
              ematch(blob, (lambda e, m: sub(e, m)
                            and (pred(e, intIn) or  # XXX why isn't this getting pulled in correctly for somas? answer: missing somas in starts? surely not, ARGH a lyph internalIn a layer
                                 pred(e, endIn) or
                                 pred(e, fasIn))),
                     start)]

    layers = [obj(t) for d in direct for t in
              ematch(blob, (lambda e, m: sub(e, m)
                            and isLayer(blob, m)
                            and (pred(e, iot) or
                                 pred(e, iots) or
                                 pred(e, onts))),
                     d)]

    lregs = []
    if layers:
        ldir = [obj(t) for d in direct for t in
                ematch(blob, (lambda e, m: sub(e, m)
                              and pred(e, layerIn)),
                       d)]

        lregs = [obj(t) for d in ldir for t in
                 ematch(blob, (lambda e, m: sub(e, m)
                               and not isLayer(blob, m)
                               and (pred(e, iot) or
                                    pred(e, iots) or  # XXX ie seems to have no hits now
                                    pred(e, onts))),
                        d)]

    regions = [obj(t) for d in direct for t in
               ematch(blob, (lambda e, m: sub(e, m)
                             and not isLayer(blob, m)
                             and (pred(e, iot) or
                                  pred(e, iots) or
                                  pred(e, onts))),
                      d)]

    #if start == 'kblad:snl16':
        #print(start, direct, layers, regions)

    # FIXME TODO nested regions and layers
    lrs = reclr(blob, start)  # FIXME doesn't pair regions and layers
    # FIXME SIGH SIGH SIGH start can be either part of a next >_<
    #print(lrs)

    assert not (lregs and regions), (lregs, regions)  # not both
    regions = lregs if lregs else regions
    #out = start, layers[0] if layers else None, regions[0] if regions else None, tuple([l for l in lrs])
    out = start, tuple(lrs)
    if out:
      return out

def render_blob(blob):
    starts = [obj(e) for e in blob['edges'] if pred(e, 'apinatomy:lyphs')]

    nxt = 'apinatomy:next'
    nxts = 'apinatomy:next*'

    nexts = [(sub(t), obj(t)) for start in starts for t in
             ematch(blob, (lambda e, m: pred(e, nxt) or pred(e, nxts)), None)]

    #pprint(nexts)

    connected_pairs = sorted(set([tuple([lay_reg(blob, e) for e in p]) for p in nexts]))
    #pprint(connected_pairs)
    return connected_pairs
sgv = Vocabulary()
sgd = scigraphd.Dynamic(cache=True, verbose=True, do_error=True)
sgc = scigraphd.Cypher(cache=True, verbose=True)
q_neru_6 = """
MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK

, (neugrp)
-[:apinatomy:links]->(link)
-[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
-[d:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph

WITH neugrp, link, lyph, a, c, d
OPTIONAL MATCH layer_ext = (lyph)
<-[d*1]-(layer)-[:apinatomy:cloneOf*0..1]->()-[:apinatomy:inheritedExternal]->()

WITH neugrp, link, lyph, a, c, d, layer_ext
OPTIONAL MATCH more = (lyph)
-[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
-[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()

WITH neugrp, link, lyph, a, c, d, layer_ext, more // there is a difference here because the previous match does not require lyphs to have external ids
MATCH (lyph)
-[e:apinatomy:ontologyTerms]->(region)

, p2 = (link)
-[:apinatomy:conveyingLyph]->(cl)
-[:apinatomy:topology]->()

, (cl)
-[x:apinatomy:inheritedExternal*0..1]->()

// use apinatomy:next to extract ordering information
, (link)
-[f:apinatomy:next|apinatomy:nextChainStartLevels*0..]->()
-[g:apinatomy:target*0..1]->()
-[h:apinatomy:rootOf*0..1]->()
-[i:apinatomy:levels*0..1]->()
<-[:apinatomy:links]-(neugrp)

// publications
WITH neugrp, a, c, d, e, f, g,h,i, p2, x, layer_ext, more
OPTIONAL MATCH path = (neugrp)
-[:apinatomy:references]->(pub)
-[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Reference"}) // cannot be curied, dynamic endpoints will not expand it

RETURN a, null as b, c, d, e, f, g,h,i, path, p2, x, layer_ext, more

UNION

// this part usually only returns the soma housing lyph
MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
-[b:apinatomy:lyphs]->(lyph)
-[c:apinatomy:internalIn]->()
-[d:apinatomy:ontologyTerms*0..1]->(region)
, p2 = (lyph)
-[:apinatomy:conveys]->(soma_link)
-[:apinatomy:source|apinatomy:target]->(soma_node)
-[:apinatomy:sourceOf]->(chain_link)
, (chain_link)
-[:apinatomy:levelIn]->(chain)
, (soma_node)
-[:apinatomy:rootOf]->(chain)

WITH lyph, a, b, c, d, p2
OPTIONAL MATCH more = (lyph)
-[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
-[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()

RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path, p2, null as x, null as layer_ext, more
"""
def neupop_blob(neupop_id):
    # XXX requires a patch to Cypher and CypherBase to support arbitrary kwargs
    blob = sgc.execute(query=q_neru_6, limit=None, neupop_id=neupop_id, output='application/json')
    return blob

def sigh():
    blobs = [neupop_blob(f"ilxtr:neuron-type-keast-{npid}") for npid in range(1, 21)]
    debs = [main(blob=blob)[0] for blob in blobs]
    simples = [render_blob(deb) for deb in debs]
    #[pprint(simple) for simple in simples[:1]]

def fbid(id):
    if id:
        result = sgv.findById(id)
        if result['labels']:
            return result['labels'][0]

def fbids(ids):
    return [(id, fbid(id)) for id in ids]

def rendlab(pairs):
    for pair in pairs:
        yield tuple((start, *(tuple(fbids(hp)) for hp in housings)) for start, housings in pair)
    #for (*tstart, sid, sasdf), (*tend, eid, easdf) in pairs:
        #yield ((*tstart, sid, fbid(sid), fbids(sasdf)) if sid else (*tstart, sid, fbids(sasdf)),
               #(*tend, eid, fbid(eid), fbids(easdf)) if eid else (*tend, eid, fbids(easdf)))
from collections import defaultdict
q_cnap = """
OPTIONAL MATCH (start:Ontology)
<-[:isDefinedBy]-(graph:NamedIndividual)
-[:type]->({iri: "https://apinatomy.org/uris/elements/Graph"})
, (start)
<-[:isDefinedBy]-(external:Class)
-[:subClassOf*]->(:Class {iri: "http://uri.interlex.org/tgbugs/uris/readable/NeuronEBM"})
return external
"""

def get_label(region):
    return id_label[region]

def get_labels(regions):
    return [get_label(region) for region in regions]

d = sgc.execute(query=q_cnap, limit=99999, output='application/json')
ids = [n['id'] for n in d['nodes']]
blobs = [neupop_blob(id) for id in ids]
id_label = {n['id']:n['lbl'] if 'lbl' in n and n['lbl'] else n['id'] for blob in blobs for n in blob['nodes']}
_ext_all = sorted(set([obj(e) for b in blobs for e in b['edges'] if pred(e, 'apinatomy:ontologyTerms')]))
ext_pop = {i:set([obj(e) for e in blob['edges'] if pred(e, 'apinatomy:ontologyTerms')]) for i, blob in zip(ids, blobs)}

# TODO popuation, region, process type, terminal
pop_by_reg_ads_trm = []

pop_by_reg = defaultdict(list)
for i, regs in ext_pop.items():
    for reg in regs:
        pop_by_reg[reg].append(i)

def key_allc(region):
    count = 0
    for i, id in enumerate(ids):
        if region in ext_pop[id]:
            count += 1
    return count

_ext_allc = sorted(_ext_all, key=key_allc)

def key_pop(pop):
    return -len(ext_pop[pop])

ids_ord = sorted(ids, key=key_pop)

def key_all(region):
    first = None
    count = 0
    regs = []
    iths = []
    for i, id in enumerate(ids_ord):
        if region in ext_pop[id]:
            regs.append(region)
            iths.append(i)
            #count += 1  # wow =+ 1 no error wat
            #if first is None:
            #first = i

    return iths[0], *[-i for i in iths[1:]]

ext_all = sorted(_ext_all, key=key_all)
n_region = len(ext_all)
n_pop = len(ext_pop)
header = [['neupop', *ext_all]]  # TODO -> label
xl = get_labels(header[0][1:])
rows = header + [[id, *[region in ext_pop[id] for region in ext_all]] for id in ids_ord]

def jac(a, b):
    anb, aub = zip(*[(ar and br, ar or br) for ar, br in zip(a, b)])
    sanb, saub = sum(anb) , sum(aub)
    if not sanb and not saub:
        return sanb  # 0
    elif not sanb:
        return saub
    elif not saub:
        return sanb
    else:
        return sanb / saub

def sumjac(rows, norm=False):
    # actually want distance from .5 ?
    mat = jac_mat(rows, switch=True)
    #pprint(mat)
    return [(sum(r[:-1]) * sum(rows[r[-1] + 1][1:]) if norm else 1, r[-1]) for i, r in enumerate(mat)]

def sumjac_norm(rows):
    return sumjac(rows, norm=True)

def jac_mat(rows, switch=False, reverse=False):
    asdf = []
    for i, (_, *rowa) in enumerate(rows[1:]):
        jacr = []
        for j, (_, *rowb) in enumerate(rows[1:]):
            jci = jac(rowa, rowb)
            if switch:
                jacr.append(jci)
            else:
                jacr.append((jci, j))
        if switch:
            jacr.append(i)
            asdf.append(jacr)
        else:
            minimized = [i for j, i in sorted(jacr)][1:]  # 1: to skip self
            minimized.append(i)
            asdf.append(minimized)

    return asdf

def jmc(rows):
    return jac_mat(rows, switch=True)

def jmcr(rows):  # XXX broken
    return jac_mat(rows, switch=True, reverse=True)

def jac_sigh(rows):
    sigh = jmc(rows)
    return [(np.std(s[:-1]), np.mean(s[:-1]), s[-1]) for s in sigh]

def jac_sigh(rows):
    sigh = jmc(rows)
    #return [(np.std(s[:-1]), np.mean(s[:-1]), s[-1]) for s in sigh]
    return [(*[((x - .5) ** 2) ** .5 for x in s[:-1]], s[-1]) for s in sigh]

def resort(rows, sortf, reverse=False):
    asdf_ord = sorted(sortf(rows), reverse=not reverse) # don't think too hard about it
    ind_rank = {j:i for i, (*_, j) in enumerate(asdf_ord)}

    def srt(row):
        ind = rows.index(row)
        if ind == 0:
            return 0
        else:
            return ind_rank[ind - 1]

    return sorted(rows, key=srt)

def sumthm(rows):
    return [[-sum(row[1:]), i] for i, row in enumerate(rows[1:])]

def sumth(rows):
    return [[sum(row[1:]), i] for i, row in enumerate(rows[1:])]

def blank(rows):
    return [[*row[1:], i] for i, row in enumerate(rows[1:])]

def sumoth(rows):
    crank = [sum(col) for col in list(zip(*rows[1:]))[1:]]
    return [[*
             #[crank[j] if c and crank[j] == max([crank[x] for x, v in enumerate(row[1:]) if v]) else 0
             [crank[j] if c else 0
             for j, c in enumerate(row[1:])], i] for i, row in enumerate(rows[1:])]

def sumothm(rows):
    crank = [sum(col) for col in list(zip(*rows[1:]))[1:]]
    return [[*
             #[crank[j] if c and crank[j] == max([crank[x] for x, v in enumerate(row[1:]) if v]) else 0
             [-crank[j] if c else 0
             for j, c in enumerate(row[1:])], i] for i, row in enumerate(rows[1:])]

def maxp(rows):
    crank = [sum(col) for col in list(zip(*rows[1:]))[1:]]
    return [[
             sorted([crank[j] if c else 0 for j, c in enumerate(row[1:])])[0],
             *[-_ for _ in sorted([crank[j] if c else 0 for j, c in enumerate(row[1:])])[1:]]
        , i]
            for i, row in enumerate(rows[1:])]

def jacb(rows):
    a = [row[1:] for row in rows[1:]]
    aT = list(zip(*a))
    # FIXME symmetry
    rd = [[jac(r1, r2) for r2 in a] for r1 in a]
    cd = [[jac(c1, c2) for c2 in aT] for c1 in aT]
    #pprint(rd)
    #pprint(cd)
    # output is a 4d matrix, from rc -> to rc
    dist = np.zeros((len(rd), len(cd), len(rd), len(cd)))
    for i, row_dists in enumerate(rd):
        for k, row_dist in enumerate(row_dists):
            for j, col_dists in enumerate(cd):
                for l, col_dist in enumerate(col_dists):
                    dist[i, j, k, l] = (row_dist ** 2 + col_dist ** 2) ** .5
    #pprint(dist)
    return dist

rows = resort(rows, sumth)
rows = list(zip(*resort(list(zip(*rows)), blank, reverse=True)))
rows = resort(rows, jmc)

if False:
    import seaborn
    seaborn.set(rc={'figure.figsize':(50, 50)})
    #seaborn.set(rc={'figure.figsize':(5, 5)})
    axes = seaborn.heatmap(
        [r[1:] for r in rows[1:]],
        xticklabels=xl,
        yticklabels=[r[0] for r in rows[1:]],
        cbar=False,
        square=True,
    )
    # axes.get_figure().savefig('heatmap-pop-reg-test.svg')
import csv
csv_path = 'heatmap-pop-reg.csv'
with open(csv_path, 'wt') as f:
    csv.writer(f).writerows(rows)
  • generate npo representation of apinatomy population location phenotypes

    undifferentiated NPO representation that matches the heatmap above

    run the blocks in the section above first

    #len(blobs)
    #pprint(id_label)
    #pprint(blobs[0])
    sorted([[a, *sorted(b)] for a, b in ext_pop.items()])
    
    from neurondm import Neuron, Phenotype, EntailedPhenotype, Config
    from pyontutils.namespaces import ilxtr, owl, skos, rdfs
    config = Config('apinatomy-locations')
    neurons = [Neuron(Phenotype(id), *(EntailedPhenotype(loc, ilxtr.hasLocationPhenotype) for loc in sorted(locs)), id_=id) for id, locs in ext_pop.items()]
    config.write()
    to_mod = [(s, skos.altLabel, o) for s, p, o in config._written_graph if p == rdfs.label]
    to_remove = [t for t in config._written_graph if t[1] in (owl.equivalentClass, ilxtr.genLabel, ilxtr.localLabel, ilxtr.simpleLabel, ilxtr.simpleLocalLabel, rdfs.label, skos.prefLabel)]
    [config._written_graph.remove(t) for t in to_remove]
    [config._written_graph.add(t) for t in to_mod]
    config._written_graph.write()
    config.write_python()
    
    from pprint import pprint
    from pyontutils.scigraph import Cypher, Vocabulary
    from pyontutils.scigraph_codegen import moduleDirect
    from pyontutils.config import auth
    scigraphd = moduleDirect(auth.get('scigraph-api'), 'scigraphd')
    from pyontutils.config import auth
    from nifstd_tools.simplify import apinat_deblob, sub, pred, obj, ematch
    from nifstd_tools.simplify import axon, dend, bag, top, ie, ies, iot, iots, ext, fasIn, endIn, layerIn, onts
    
    ext = onts  # XXX schema change
    
    
    def query(neupop_id):
        import requests
        url = ('https://scicrunch.org/api/1/sparc-scigraph/scigraph/dynamic/'
               f'demos/apinat/neru-7/{neupop_id}?limit=9999999&key={auth.get("scigraph-api-key")}')
        resp = requests.get(url, headers={'Accept': 'application/json'})
        blob = resp.json()
        return blob
    
    
    def find_terminals(blob, type):
        return [es for es in blob['edges']
                if
                pred(es, iots) and
                obj(es, type) and
                ematch(blob,
                       (lambda e, m:
                        sub(e, m) and
                        pred(e, top) and
                        obj(e, bag)),
                       sub(es))]
    
    
    def find_region(blob, edge):
        collect = []
        def select(e, m, collect=collect):
            if sub(e, m):
                #print(e)
                if pred(e, layerIn) or pred(e, fasIn) or pred(e, endIn):
                    return ematch(blob, select, obj(e))
                elif pred(e, onts):
                    region = obj(e)
                    collect.extend([b for b in blob['nodes'] if b['id'] == region])
                    return region
    
        ematch(blob, select, sub(edge))
        return collect
    
    
    def find_region_layer(blob, edge, bindex):
        collect = []
        layers = []
        layers_iots = []
        donel = set()
        doner = set()
        def select_term(e, m, layers=layers):
            if sub(e, m):
                pprint(e)
                if (pred(e, onts)
                    or pred(e, iots)):
                    layer = obj(e)
                    if layer not in donel:
                        donel.add(layer)
                        if pred(e, iots):
                            layers_iots.append(bindex[layer])
                        else:
                            layers.append(bindex[layer])
                    return layer
    
        def select(e, m, collect=collect):
            if sub(e, m):
                if pred(e, layerIn):
                    # we're at a layer
                    ematch(blob, select_term, sub(e))
                    return ematch(blob, select, obj(e))
                elif (pred(e, fasIn)
                      or pred(e, endIn)):
                    return ematch(blob, select, obj(e))
                elif pred(e, onts):
                    region = obj(e)
                    if region not in doner:
                        doner.add(region)
                        collect.append(bindex[region])
                    return region
        ematch(blob, select, sub(edge))
        if collect and not layers and not layers_iots:
            layers = [None]
        elif layers_iots and not layers:
            layers = layers_iots
    
        if len(collect) != len(layers):
            raise ValueError(f'len not matched {len(collect)} {len(layers)}')
        return list(zip(collect, layers))
    
    
    def find_terminal_regions(blob, type):
        return [region for es in find_terminals(blob, type) for region in find_region(blob, es)]
    
    
    def find_terminal_region_layers(blob, type, bindex):
        return [(region, layer) for es in find_terminals(blob, type)
                for region, layer in find_region_layer(blob, es, bindex)]
    
    
    def main(blob=None, blob_string=None, neupop_id=None, remove_converge=remove):
        # do not call this function unless it is from an org babel block, there are free variables
        if blob is None and blob_string is not None:
            import json
            blob = json.loads(blob_string)
    
        neupop_id = neupop_id if neupop_id else 'ilxtr:neuron-type-keast-6'
    
        if blob is None:
            blob = query(neupop_id)
    
        blob, edges, somas, externals, ordering_edges = v = apinat_deblob(
            blob, remove_converge=remove_converge)
    
        test_call = False  # change to True to test the #+call: expression
        if test_call:
            pprint(v)
        return v
    intIn = 'apinatomy:internalIn'
    
    def isLayer(blob, s):
        return ematch(blob, (lambda e, m: sub(e, m) and pred(e, layerIn)), s)
    
    def reclr(blob, start_link):
        # recurse up the hierarchy until fasIn endIn intIn terminates
        #print('starting reclr')
        collect = []
        layer = []
        col = True
        def select_ext(e, m, collect=collect):
            nonlocal col
            nonlocal layer
            if sub(e, m):
                if pred(e, 'apinatomy:cloneOf'):  # should be zapped during simplify
                    return ematch(blob, select_ext, obj(e))
                if pred(e, onts) or pred(e, iot) or pred(e, iots):
                    #print('select_ext', e)
                    external = obj(e)
                    #print('se', col, layer)
                    if col:
                        if layer:
                            if len(layer) > 1:
                                l, *layer = layer
                            else:
                                l = layer.pop()
                        else:
                            l = None
                        r = [b for b in blob['nodes'] if b['id'] == external][0]['id']  # if this is empty we are in big trouble
                        collect.append((l, r))
                    else:
                        l = [b for b in blob['nodes'] if b['id'] == external][0]['id']
                        layer.append(l)
                    #print(layer)
                    return external
    
        def select(e, m):
            nonlocal col
            if sub(e, m):
                #print(e)
                if pred(e, layerIn) or pred(e, fasIn) or pred(e, endIn) or pred(e, intIn):
                    #print('select', e)
                    col = not isLayer(blob, obj(e))
                    #print('s', col)
                    ematch(blob, select_ext, obj(e))
                    ematch(blob, select, obj(e))
    
        ematch(blob, select, start_link)
    
        return collect
    
    def lay_reg(blob, start):
        direct = [obj(t) for t in
                  ematch(blob, (lambda e, m: sub(e, m)
                                and (pred(e, intIn) or  # XXX why isn't this getting pulled in correctly for somas? answer: missing somas in starts? surely not, ARGH a lyph internalIn a layer
                                     pred(e, endIn) or
                                     pred(e, fasIn))),
                         start)]
    
        layers = [obj(t) for d in direct for t in
                  ematch(blob, (lambda e, m: sub(e, m)
                                and isLayer(blob, m)
                                and (pred(e, iot) or
                                     pred(e, iots) or
                                     pred(e, onts))),
                         d)]
    
        lregs = []
        if layers:
            ldir = [obj(t) for d in direct for t in
                    ematch(blob, (lambda e, m: sub(e, m)
                                  and pred(e, layerIn)),
                           d)]
    
            lregs = [obj(t) for d in ldir for t in
                     ematch(blob, (lambda e, m: sub(e, m)
                                   and not isLayer(blob, m)
                                   and (pred(e, iot) or
                                        pred(e, iots) or  # XXX ie seems to have no hits now
                                        pred(e, onts))),
                            d)]
    
        regions = [obj(t) for d in direct for t in
                   ematch(blob, (lambda e, m: sub(e, m)
                                 and not isLayer(blob, m)
                                 and (pred(e, iot) or
                                      pred(e, iots) or
                                      pred(e, onts))),
                          d)]
    
        #if start == 'kblad:snl16':
            #print(start, direct, layers, regions)
    
        # FIXME TODO nested regions and layers
        lrs = reclr(blob, start)  # FIXME doesn't pair regions and layers
        # FIXME SIGH SIGH SIGH start can be either part of a next >_<
        #print(lrs)
    
        assert not (lregs and regions), (lregs, regions)  # not both
        regions = lregs if lregs else regions
        #out = start, layers[0] if layers else None, regions[0] if regions else None, tuple([l for l in lrs])
        out = start, tuple(lrs)
        if out:
          return out
    
    def render_blob(blob):
        starts = [obj(e) for e in blob['edges'] if pred(e, 'apinatomy:lyphs')]
    
        nxt = 'apinatomy:next'
        nxts = 'apinatomy:next*'
    
        nexts = [(sub(t), obj(t)) for start in starts for t in
                 ematch(blob, (lambda e, m: pred(e, nxt) or pred(e, nxts)), None)]
    
        #pprint(nexts)
    
        connected_pairs = sorted(set([tuple([lay_reg(blob, e) for e in p]) for p in nexts]))
        #pprint(connected_pairs)
        return connected_pairs
    sgv = Vocabulary()
    sgd = scigraphd.Dynamic(cache=True, verbose=True, do_error=True)
    sgc = scigraphd.Cypher(cache=True, verbose=True)
    q_neru_6 = """
    MATCH (neupop:Class{iri: $neupop_id})
    -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
    
    , (neugrp)
    -[:apinatomy:links]->(link)
    -[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
    -[d:apinatomy:layerIn*0..1]->(lyph)
    -[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph
    
    WITH neugrp, link, lyph, a, c, d
    OPTIONAL MATCH layer_ext = (lyph)
    <-[d*1]-(layer)-[:apinatomy:cloneOf*0..1]->()-[:apinatomy:inheritedExternal]->()
    
    WITH neugrp, link, lyph, a, c, d, layer_ext
    OPTIONAL MATCH more = (lyph)
    -[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
    -[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()
    
    WITH neugrp, link, lyph, a, c, d, layer_ext, more // there is a difference here because the previous match does not require lyphs to have external ids
    MATCH (lyph)
    -[e:apinatomy:ontologyTerms]->(region)
    
    , p2 = (link)
    -[:apinatomy:conveyingLyph]->(cl)
    -[:apinatomy:topology]->()
    
    , (cl)
    -[x:apinatomy:inheritedExternal*0..1]->()
    
    // use apinatomy:next to extract ordering information
    , (link)
    -[f:apinatomy:next|apinatomy:nextChainStartLevels*0..]->()
    -[g:apinatomy:target*0..1]->()
    -[h:apinatomy:rootOf*0..1]->()
    -[i:apinatomy:levels*0..1]->()
    <-[:apinatomy:links]-(neugrp)
    
    // publications
    WITH neugrp, a, c, d, e, f, g,h,i, p2, x, layer_ext, more
    OPTIONAL MATCH path = (neugrp)
    -[:apinatomy:references]->(pub)
    -[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Reference"}) // cannot be curied, dynamic endpoints will not expand it
    
    RETURN a, null as b, c, d, e, f, g,h,i, path, p2, x, layer_ext, more
    
    UNION
    
    // this part usually only returns the soma housing lyph
    MATCH (neupop:Class{iri: $neupop_id})
    -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
    -[b:apinatomy:lyphs]->(lyph)
    -[c:apinatomy:internalIn]->()
    -[d:apinatomy:ontologyTerms*0..1]->(region)
    , p2 = (lyph)
    -[:apinatomy:conveys]->(soma_link)
    -[:apinatomy:source|apinatomy:target]->(soma_node)
    -[:apinatomy:sourceOf]->(chain_link)
    , (chain_link)
    -[:apinatomy:levelIn]->(chain)
    , (soma_node)
    -[:apinatomy:rootOf]->(chain)
    
    WITH lyph, a, b, c, d, p2
    OPTIONAL MATCH more = (lyph)
    -[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
    -[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()
    
    RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path, p2, null as x, null as layer_ext, more
    """
    def neupop_blob(neupop_id):
        # XXX requires a patch to Cypher and CypherBase to support arbitrary kwargs
        blob = sgc.execute(query=q_neru_6, limit=None, neupop_id=neupop_id, output='application/json')
        return blob
    
    def sigh():
        blobs = [neupop_blob(f"ilxtr:neuron-type-keast-{npid}") for npid in range(1, 21)]
        debs = [main(blob=blob)[0] for blob in blobs]
        simples = [render_blob(deb) for deb in debs]
        #[pprint(simple) for simple in simples[:1]]
    
    def fbid(id):
        if id:
            result = sgv.findById(id)
            if result['labels']:
                return result['labels'][0]
    
    def fbids(ids):
        return [(id, fbid(id)) for id in ids]
    
    def rendlab(pairs):
        for pair in pairs:
            yield tuple((start, *(tuple(fbids(hp)) for hp in housings)) for start, housings in pair)
        #for (*tstart, sid, sasdf), (*tend, eid, easdf) in pairs:
            #yield ((*tstart, sid, fbid(sid), fbids(sasdf)) if sid else (*tstart, sid, fbids(sasdf)),
                   #(*tend, eid, fbid(eid), fbids(easdf)) if eid else (*tend, eid, fbids(easdf)))
    from collections import defaultdict
    q_cnap = """
    OPTIONAL MATCH (start:Ontology)
    <-[:isDefinedBy]-(graph:NamedIndividual)
    -[:type]->({iri: "https://apinatomy.org/uris/elements/Graph"})
    , (start)
    <-[:isDefinedBy]-(external:Class)
    -[:subClassOf*]->(:Class {iri: "http://uri.interlex.org/tgbugs/uris/readable/NeuronEBM"})
    return external
    """
    
    def get_label(region):
        return id_label[region]
    
    def get_labels(regions):
        return [get_label(region) for region in regions]
    
    d = sgc.execute(query=q_cnap, limit=99999, output='application/json')
    ids = [n['id'] for n in d['nodes']]
    blobs = [neupop_blob(id) for id in ids]
    id_label = {n['id']:n['lbl'] if 'lbl' in n and n['lbl'] else n['id'] for blob in blobs for n in blob['nodes']}
    _ext_all = sorted(set([obj(e) for b in blobs for e in b['edges'] if pred(e, 'apinatomy:ontologyTerms')]))
    ext_pop = {i:set([obj(e) for e in blob['edges'] if pred(e, 'apinatomy:ontologyTerms')]) for i, blob in zip(ids, blobs)}
    
    # TODO popuation, region, process type, terminal
    pop_by_reg_ads_trm = []
    
    pop_by_reg = defaultdict(list)
    for i, regs in ext_pop.items():
        for reg in regs:
            pop_by_reg[reg].append(i)
    
    def key_allc(region):
        count = 0
        for i, id in enumerate(ids):
            if region in ext_pop[id]:
                count += 1
        return count
    
    _ext_allc = sorted(_ext_all, key=key_allc)
    
    def key_pop(pop):
        return -len(ext_pop[pop])
    
    ids_ord = sorted(ids, key=key_pop)
    
    def key_all(region):
        first = None
        count = 0
        regs = []
        iths = []
        for i, id in enumerate(ids_ord):
            if region in ext_pop[id]:
                regs.append(region)
                iths.append(i)
                #count += 1  # wow =+ 1 no error wat
                #if first is None:
                #first = i
    
        return iths[0], *[-i for i in iths[1:]]
    
    ext_all = sorted(_ext_all, key=key_all)
    n_region = len(ext_all)
    n_pop = len(ext_pop)
    header = [['neupop', *ext_all]]  # TODO -> label
    xl = get_labels(header[0][1:])
    rows = header + [[id, *[region in ext_pop[id] for region in ext_all]] for id in ids_ord]
    
    def jac(a, b):
        anb, aub = zip(*[(ar and br, ar or br) for ar, br in zip(a, b)])
        sanb, saub = sum(anb) , sum(aub)
        if not sanb and not saub:
            return sanb  # 0
        elif not sanb:
            return saub
        elif not saub:
            return sanb
        else:
            return sanb / saub
    
    def sumjac(rows, norm=False):
        # actually want distance from .5 ?
        mat = jac_mat(rows, switch=True)
        #pprint(mat)
        return [(sum(r[:-1]) * sum(rows[r[-1] + 1][1:]) if norm else 1, r[-1]) for i, r in enumerate(mat)]
    
    def sumjac_norm(rows):
        return sumjac(rows, norm=True)
    
    def jac_mat(rows, switch=False, reverse=False):
        asdf = []
        for i, (_, *rowa) in enumerate(rows[1:]):
            jacr = []
            for j, (_, *rowb) in enumerate(rows[1:]):
                jci = jac(rowa, rowb)
                if switch:
                    jacr.append(jci)
                else:
                    jacr.append((jci, j))
            if switch:
                jacr.append(i)
                asdf.append(jacr)
            else:
                minimized = [i for j, i in sorted(jacr)][1:]  # 1: to skip self
                minimized.append(i)
                asdf.append(minimized)
    
        return asdf
    
    def jmc(rows):
        return jac_mat(rows, switch=True)
    
    def jmcr(rows):  # XXX broken
        return jac_mat(rows, switch=True, reverse=True)
    
    def jac_sigh(rows):
        sigh = jmc(rows)
        return [(np.std(s[:-1]), np.mean(s[:-1]), s[-1]) for s in sigh]
    
    def jac_sigh(rows):
        sigh = jmc(rows)
        #return [(np.std(s[:-1]), np.mean(s[:-1]), s[-1]) for s in sigh]
        return [(*[((x - .5) ** 2) ** .5 for x in s[:-1]], s[-1]) for s in sigh]
    
    def resort(rows, sortf, reverse=False):
        asdf_ord = sorted(sortf(rows), reverse=not reverse) # don't think too hard about it
        ind_rank = {j:i for i, (*_, j) in enumerate(asdf_ord)}
    
        def srt(row):
            ind = rows.index(row)
            if ind == 0:
                return 0
            else:
                return ind_rank[ind - 1]
    
        return sorted(rows, key=srt)
    
    def sumthm(rows):
        return [[-sum(row[1:]), i] for i, row in enumerate(rows[1:])]
    
    def sumth(rows):
        return [[sum(row[1:]), i] for i, row in enumerate(rows[1:])]
    
    def blank(rows):
        return [[*row[1:], i] for i, row in enumerate(rows[1:])]
    
    def sumoth(rows):
        crank = [sum(col) for col in list(zip(*rows[1:]))[1:]]
        return [[*
                 #[crank[j] if c and crank[j] == max([crank[x] for x, v in enumerate(row[1:]) if v]) else 0
                 [crank[j] if c else 0
                 for j, c in enumerate(row[1:])], i] for i, row in enumerate(rows[1:])]
    
    def sumothm(rows):
        crank = [sum(col) for col in list(zip(*rows[1:]))[1:]]
        return [[*
                 #[crank[j] if c and crank[j] == max([crank[x] for x, v in enumerate(row[1:]) if v]) else 0
                 [-crank[j] if c else 0
                 for j, c in enumerate(row[1:])], i] for i, row in enumerate(rows[1:])]
    
    def maxp(rows):
        crank = [sum(col) for col in list(zip(*rows[1:]))[1:]]
        return [[
                 sorted([crank[j] if c else 0 for j, c in enumerate(row[1:])])[0],
                 *[-_ for _ in sorted([crank[j] if c else 0 for j, c in enumerate(row[1:])])[1:]]
            , i]
                for i, row in enumerate(rows[1:])]
    
    def jacb(rows):
        a = [row[1:] for row in rows[1:]]
        aT = list(zip(*a))
        # FIXME symmetry
        rd = [[jac(r1, r2) for r2 in a] for r1 in a]
        cd = [[jac(c1, c2) for c2 in aT] for c1 in aT]
        #pprint(rd)
        #pprint(cd)
        # output is a 4d matrix, from rc -> to rc
        dist = np.zeros((len(rd), len(cd), len(rd), len(cd)))
        for i, row_dists in enumerate(rd):
            for k, row_dist in enumerate(row_dists):
                for j, col_dists in enumerate(cd):
                    for l, col_dist in enumerate(col_dists):
                        dist[i, j, k, l] = (row_dist ** 2 + col_dist ** 2) ** .5
        #pprint(dist)
        return dist
    
    rows = resort(rows, sumth)
    rows = list(zip(*resort(list(zip(*rows)), blank, reverse=True)))
    rows = resort(rows, jmc)
    
    if False:
        import seaborn
        seaborn.set(rc={'figure.figsize':(50, 50)})
        #seaborn.set(rc={'figure.figsize':(5, 5)})
        axes = seaborn.heatmap(
            [r[1:] for r in rows[1:]],
            xticklabels=xl,
            yticklabels=[r[0] for r in rows[1:]],
            cbar=False,
            square=True,
        )
        # axes.get_figure().savefig('heatmap-pop-reg-test.svg')
    from neurondm import Neuron, Phenotype, EntailedPhenotype, Config
    from pyontutils.namespaces import ilxtr, owl, skos, rdfs
    config = Config('apinatomy-locations')
    neurons = [Neuron(Phenotype(id), *(EntailedPhenotype(loc, ilxtr.hasLocationPhenotype) for loc in sorted(locs)), id_=id) for id, locs in ext_pop.items()]
    config.write()
    to_mod = [(s, skos.altLabel, o) for s, p, o in config._written_graph if p == rdfs.label]
    to_remove = [t for t in config._written_graph if t[1] in (owl.equivalentClass, ilxtr.genLabel, ilxtr.localLabel, ilxtr.simpleLabel, ilxtr.simpleLocalLabel, rdfs.label, skos.prefLabel)]
    [config._written_graph.remove(t) for t in to_remove]
    [config._written_graph.add(t) for t in to_mod]
    config._written_graph.write()
    config.write_python()
    

region by region

SciGraph cypher queries

These can be tested against the execute endpoint as well (if the SciGraph server is running the patch to align the behavior).

neru

neru-7

Switch over to apinat:inheritedOntologyTerms.

MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK

, (neugrp)
-[:apinatomy:links]->(link)
-[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
-[d:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph

WITH neugrp, link, lyph, a, c, d
OPTIONAL MATCH layer_ext = (lyph)
<-[d*1]-(layer)-[:apinatomy:cloneOf*0..1]->()-[:apinatomy:inheritedOntologyTerms]->()

WITH neugrp, link, lyph, a, c, d, layer_ext
OPTIONAL MATCH more = (lyph)
-[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
-[:apinatomy:ontologyTerms|apinatomy:inheritedOntologyTerms]->()

WITH neugrp, link, lyph, a, c, d, layer_ext, more // there is a difference here because the previous match does not require lyphs to have external ids
MATCH (lyph)
-[e:apinatomy:ontologyTerms]->(region)

, p2 = (link)
-[:apinatomy:conveyingLyph]->(cl)
-[:apinatomy:topology]->()

, (cl)
-[x:apinatomy:inheritedOntologyTerms*0..1]->()

// use apinatomy:next to extract ordering information
, (link)
-[f:apinatomy:next|apinatomy:nextChainStartLevels*0..]->()
-[g:apinatomy:target*0..1]->()
-[h:apinatomy:rootOf*0..1]->()
-[i:apinatomy:levels*0..1]->()
<-[:apinatomy:links]-(neugrp)

// publications
WITH neugrp, a, c, d, e, f, g,h,i, p2, x, layer_ext, more
OPTIONAL MATCH path = (neugrp)
-[:apinatomy:references]->(pub)
-[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Reference"}) // cannot be curied, dynamic endpoints will not expand it

RETURN a, null as b, c, d, e, f, g,h,i, path, p2, x, layer_ext, more

UNION

// this part usually only returns the soma housing lyph
MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
-[b:apinatomy:lyphs]->(lyph)
-[c:apinatomy:internalIn]->()
-[d:apinatomy:ontologyTerms*0..1]->(region)
, p2 = (lyph)
-[:apinatomy:conveys]->(soma_link)
-[:apinatomy:source|apinatomy:target]->(soma_node)
-[:apinatomy:sourceOf]->(chain_link)
, (chain_link)
-[:apinatomy:levelIn]->(chain)
, (soma_node)
-[:apinatomy:rootOf]->(chain)

WITH lyph, a, b, c, d, p2
OPTIONAL MATCH more = (lyph)
-[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
-[:apinatomy:ontologyTerms|apinatomy:inheritedOntologyTerms]->()

RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path, p2, null as x, null as layer_ext, more

neru-6

The only difference between 5 and 6 is that 6 also traverses apinat:nextChainStartLevels

MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK

, (neugrp)
-[:apinatomy:links]->(link)
-[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
-[d:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph

WITH neugrp, link, lyph, a, c, d
OPTIONAL MATCH layer_ext = (lyph)
<-[d*1]-(layer)-[:apinatomy:cloneOf*0..1]->()-[:apinatomy:inheritedExternal]->()

WITH neugrp, link, lyph, a, c, d, layer_ext
OPTIONAL MATCH more = (lyph)
-[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
-[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()

WITH neugrp, link, lyph, a, c, d, layer_ext, more // there is a difference here because the previous match does not require lyphs to have external ids
MATCH (lyph)
-[e:apinatomy:ontologyTerms]->(region)

, p2 = (link)
-[:apinatomy:conveyingLyph]->(cl)
-[:apinatomy:topology]->()

, (cl)
-[x:apinatomy:inheritedExternal*0..1]->()

// use apinatomy:next to extract ordering information
, (link)
-[f:apinatomy:next|apinatomy:nextChainStartLevels*0..]->()
-[g:apinatomy:target*0..1]->()
-[h:apinatomy:rootOf*0..1]->()
-[i:apinatomy:levels*0..1]->()
<-[:apinatomy:links]-(neugrp)

// publications
WITH neugrp, a, c, d, e, f, g,h,i, p2, x, layer_ext, more
OPTIONAL MATCH path = (neugrp)
-[:apinatomy:references]->(pub)
-[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Reference"}) // cannot be curied, dynamic endpoints will not expand it

RETURN a, null as b, c, d, e, f, g,h,i, path, p2, x, layer_ext, more

UNION

// this part usually only returns the soma housing lyph
MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
-[b:apinatomy:lyphs]->(lyph)
-[c:apinatomy:internalIn]->()
-[d:apinatomy:ontologyTerms*0..1]->(region)
, p2 = (lyph)
-[:apinatomy:conveys]->(soma_link)
-[:apinatomy:source|apinatomy:target]->(soma_node)
-[:apinatomy:sourceOf]->(chain_link)
, (chain_link)
-[:apinatomy:levelIn]->(chain)
, (soma_node)
-[:apinatomy:rootOf]->(chain)

WITH lyph, a, b, c, d, p2
OPTIONAL MATCH more = (lyph)
-[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
-[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()

RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path, p2, null as x, null as layer_ext, more

neru-6-aacar-12.svg

neru-5

  • query
    MATCH (neupop:Class{iri: $neupop_id})
    -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
    
    , (neugrp)
    -[:apinatomy:links]->(link)
    -[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
    -[d:apinatomy:layerIn*0..1]->(lyph)
    -[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph
    
    WITH neugrp, link, lyph, a, c, d
    OPTIONAL MATCH layer_ext = (lyph)
    <-[d*1]-(layer)-[:apinatomy:cloneOf*0..1]->()-[:apinatomy:inheritedExternal]->()
    
    WITH neugrp, link, lyph, a, c, d, layer_ext
    OPTIONAL MATCH more = (lyph)
    -[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
    -[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()
    
    WITH neugrp, link, lyph, a, c, d, layer_ext, more // there is a difference here because the previous match does not require lyphs to have external ids
    MATCH (lyph)
    -[e:apinatomy:ontologyTerms]->(region)
    
    , p2 = (link)
    -[:apinatomy:conveyingLyph]->(cl)
    -[:apinatomy:topology]->()
    
    , (cl)
    -[x:apinatomy:inheritedExternal*0..1]->()
    
    // use apinatomy:next to extract ordering information
    , (link)
    -[f:apinatomy:next*0..]->()
    -[g:apinatomy:target*0..1]->()
    -[h:apinatomy:rootOf*0..1]->()
    -[i:apinatomy:levels*0..1]->()
    <-[:apinatomy:links]-(neugrp)
    
    // publications
    WITH neugrp, a, c, d, e, f, g,h,i, p2, x, layer_ext, more
    OPTIONAL MATCH path = (neugrp)
    -[:apinatomy:references]->(pub)
    -[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Reference"}) // cannot be curied, dynamic endpoints will not expand it
    
    RETURN a, null as b, c, d, e, f, g,h,i, path, p2, x, layer_ext, more
    
    UNION
    
    // this part usually only returns the soma housing lyph
    MATCH (neupop:Class{iri: $neupop_id})
    -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
    -[b:apinatomy:lyphs]->(lyph)
    -[c:apinatomy:internalIn]->()
    -[d:apinatomy:ontologyTerms*0..1]->(region)
    , p2 = (lyph)
    -[:apinatomy:conveys]->(soma_link)
    -[:apinatomy:source|apinatomy:target]->(soma_node)
    -[:apinatomy:sourceOf]->(chain_link)
    , (chain_link)
    -[:apinatomy:levelIn]->(chain)
    , (soma_node)
    -[:apinatomy:rootOf]->(chain)
    
    WITH lyph, a, b, c, d, p2
    OPTIONAL MATCH more = (lyph)
    -[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
    -[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()
    
    RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path, p2, null as x, null as layer_ext, more
    

    neru-5-keast-6.svg

    MATCH (neupop:Class{iri: $neupop_id})
    -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
    
    , (neugrp)
    -[:apinatomy:links]->(link)
    -[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
    -[d:apinatomy:layerIn*0..1]->(lyph)
    -[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph
    
    WITH neugrp, link, lyph, a, c, d
    OPTIONAL MATCH layer_ext = (lyph)
    <-[d*1]-(layer)-[:apinatomy:cloneOf*0..1]->()-[:apinatomy:inheritedExternal]->()
    
    WITH neugrp, link, lyph, a, c, d, layer_ext
    OPTIONAL MATCH more = (lyph)
    -[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
    -[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()
    
    WITH neugrp, link, lyph, a, c, d, layer_ext, more // there is a difference here because the previous match does not require lyphs to have external ids
    MATCH (lyph)
    -[e:apinatomy:ontologyTerms]->(region)
    
    , p2 = (link)
    -[:apinatomy:conveyingLyph]->(cl)
    -[:apinatomy:topology]->()
    
    , (cl)
    -[x:apinatomy:inheritedExternal*0..1]->()
    
    // use apinatomy:next to extract ordering information
    , (link)
    
    -[g:apinatomy:target*0..1]->()
    -[h:apinatomy:rootOf*0..1]->()
    -[i:apinatomy:levels*0..1]->()
    <-[:apinatomy:links]-(neugrp)
    
    // publications
    WITH neugrp, a, c, d, e, f, g,h,i, p2, x, layer_ext, more
    OPTIONAL MATCH path = (neugrp)
    -[:apinatomy:references]->(pub)
    -[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Reference"}) // cannot be curied, dynamic endpoints will not expand it
    
    RETURN a, null as b, c, d, e, f, g,h,i, path, p2, x, layer_ext, more
    
    UNION
    
    // this part usually only returns the soma housing lyph
    MATCH (neupop:Class{iri: $neupop_id})
    -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
    -[b:apinatomy:lyphs]->(lyph)
    -[c:apinatomy:internalIn]->()
    -[d:apinatomy:ontologyTerms*0..1]->(region)
    , p2 = (lyph)
    -[:apinatomy:conveys]->(soma_link)
    -[:apinatomy:source|apinatomy:target]->(soma_node)
    -[:apinatomy:sourceOf]->(chain_link)
    , (chain_link)
    -[:apinatomy:levelIn]->(chain)
    , (soma_node)
    -[:apinatomy:rootOf]->(chain)
    
    WITH lyph, a, b, c, d, p2
    OPTIONAL MATCH more = (lyph)
    -[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
    -[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()
    
    RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path, p2, null as x, null as layer_ext, more
    

neru-4

MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK

, (neugrp)
-[:apinatomy:links]->(link)
-[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
-[d:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph

WITH neugrp, link, lyph, a, c, d
OPTIONAL MATCH layer_ext = (lyph)
<-[d*1]-(layer)-[:apinatomy:cloneOf]->()-[:apinatomy:inheritedOntologyTerms]->()

WITH neugrp, link, lyph, a, c, d, layer_ext // there is a difference here because the previous match does not require lyphs to have external ids
MATCH (lyph)
-[e:apinatomy:ontologyTerms]->(region)

, p2 = (link)
-[:apinatomy:conveyingLyph]->(cl)
-[:apinatomy:topology]->()

, (cl)
-[x:apinatomy:inheritedOntologyTerms*0..1]->()

// use apinatomy:next to extract ordering information
, (link)
-[f:apinatomy:next*0..]->()
-[g:apinatomy:target*0..1]->()
-[h:apinatomy:rootOf*0..1]->()
-[i:apinatomy:levels*0..1]->()
<-[:apinatomy:links]-(neugrp)

// publications
WITH neugrp, a, c, d, e, f, g,h,i, p2, x, layer_ext
OPTIONAL MATCH path = (neugrp)
-[:apinatomy:references]->(pub)
-[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Publication"}) // cannot be curied, dynamic endpoints will not expand it

RETURN a, null as b, c, d, e, f, g,h,i, path, p2, x, layer_ext

UNION

// this part usually only returns the soma housing lyph
MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
-[b:apinatomy:lyphs]->(lyph)
-[c:apinatomy:internalIn]->()
-[d:apinatomy:ontologyTerms*0..1]->(region)
, p2 = (lyph)
-[:apinatomy:conveys]->(soma_link)
-[:apinatomy:source|apinatomy:target]->(soma_node)
-[:apinatomy:sourceOf]->(chain_link)
, (chain_link)
-[:apinatomy:levelIn]->(chain)
, (soma_node)
-[:apinatomy:rootOf]->(chain)

RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path, p2, null as x, null as layer_ext

neru-4.svg

neru-3

MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK

// publications
WITH neugrp, a
OPTIONAL MATCH path = (neugrp)
-[:apinatomy:references]->(pub)
-[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Publication"}) // cannot be curied, dynamic endpoints will not expand it

WITH neugrp, a, path
MATCH (neugrp)
-[b:apinatomy:links]->(link)
-[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
-[d:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph

WITH neugrp, link, lyph, a, b, c, d, path // MATCH vs , not all things that match as lyphs have externals
MATCH (lyph)
-[e:apinatomy:ontologyTerms]->(region)

// use apinatomy:next to extract ordering information
WITH neugrp, link, a, b, c, d, e, path
MATCH p2 = (link)
-[:apinatomy:conveyingLyph]->(cl)
-[:apinatomy:topology]->()

WITH neugrp, link, a, b, c, d, e, path, p2, cl
MATCH (cl)
-[x:apinatomy:inheritedOntologyTerms*0..1]->()

WITH neugrp, link, a, b, c, d, e, path, p2, x
MATCH (link)
-[f:apinatomy:next*0..]->()
//-[f:apinatomy:next|apinatomy:nextChainStartLevels*0..]->()
// FIXME these should be collapsing into a single relationship
-[g:apinatomy:target*0..1]->()
-[h:apinatomy:rootOf*0..1]->()
-[i:apinatomy:levels*0..1]->()
<-[:apinatomy:links]-(neugrp)

RETURN a, b, c, d, e, f, g,h,i, path, p2, x

UNION

// this part usually only returns the soma housing lyph
MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
-[b:apinatomy:lyphs]->(lyph)
-[c:apinatomy:internalIn]->()
-[d:apinatomy:ontologyTerms*0..1]->(region)
// this variant shows the dead end lyphs that correspond to the fasciculatesIn links above
//-[c:apinatomy:internalIn*0..1]->()
//-[d:apinatomy:ontologyTerms*0..1]->(region)
RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path, null as p2, null as x

neru-3.svg

neru-2

MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK

// publications
WITH neugrp, a
OPTIONAL MATCH path = (neugrp)
-[:apinatomy:references]->(pub)
-[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Publication"}) // cannot be curied, dynamic endpoints will not expand it

WITH neugrp, a, path
MATCH (neugrp)
-[b:apinatomy:links]->(link)
-[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
-[d:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph

WITH neugrp, link, lyph, a, b, c, d, path
MATCH (lyph)
-[e:apinatomy:ontologyTerms]->(region)

// use apinatomy:next to extract ordering information
WITH neugrp, link, a, b, c, d, e, path
MATCH (link)
-[f:apinatomy:next*0..]->()
//-[f:apinatomy:next|apinatomy:nextChainStartLevels*0..]->()
// FIXME these should be collapsing into a single relationship
-[g:apinatomy:target*0..1]->()
-[h:apinatomy:rootOf*0..1]->()
-[i:apinatomy:levels*0..1]->()
<-[:apinatomy:links]-(neugrp)

RETURN a, b, c, d, e, f, g,h,i, path

UNION

// this part usually only returns the soma housing lyph
MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
-[b:apinatomy:lyphs]->(lyph)
-[c:apinatomy:internalIn]->(e) // e is a hack to get columns to match
-[d:apinatomy:ontologyTerms*0..1]->(region)
// this variant shows the dead end lyphs that correspond to the fasciculatesIn links above
//-[c:apinatomy:internalIn*0..1]->(e)
//-[d:apinatomy:ontologyTerms*0..1]->(region)
RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path

neru-2.svg

MATCH path = (neupop:Class{iri: $neupop_id})
-[:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"})
-[:apinatomy:links]->()
-[:apinatomy:next*0..]->()
// -[:apinatomy:nextChainStartLevels*0..]-()
//-[:apinatomy:prevChainEndLevels*0..]-()
-[:apinatomy:levelIn*0..]->()
RETURN path

neru-debug.svg

neru-1

MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK

// publications
WITH neugrp, a
OPTIONAL MATCH path = (neugrp)
-[:apinatomy:references]->(pub)
-[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Publication"}) // cannot be curied, dynamic endpoints will not expand it

WITH neugrp, a, path
MATCH (neugrp)
-[b:apinatomy:links]->(link)
-[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
-[d:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph

WITH lyph, a, b, c, d, path
MATCH (lyph)
-[e:apinatomy:ontologyTerms]->(region)
RETURN a, b, c, d, e, path

UNION

// this part usually only returns the soma housing lyph
MATCH (neupop:Class{iri: $neupop_id})
-[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
-[b:apinatomy:lyphs]->(lyph)
-[c:apinatomy:internalIn]->(e) // e is a hack to get columns to match
-[d:apinatomy:ontologyTerms*0..1]->(region)
// this variant shows the dead end lyphs that correspond to the fasciculatesIn links above
//-[c:apinatomy:internalIn*0..1]->(e)
//-[d:apinatomy:ontologyTerms*0..1]->(region)
return a, b, c, d, null AS e, null AS path

neru-1.svg

all populations

OPTIONAL MATCH (start:Ontology)
<-[:isDefinedBy]-(graph:NamedIndividual)
-[:type]->({iri: "https://apinatomy.org/uris/elements/Graph"})
, (start)
<-[:isDefinedBy]-(external:Class)
-[:subClassOf*]->(:Class {iri: "http://uri.interlex.org/tgbugs/uris/readable/NeuronEBM"})
return external

neru populations

List all neuron populations that appear in some ApiNATOMY model.

MATCH (start:Ontology)
<-[:isDefinedBy]-(external:Class)
-[:subClassOf*]->(:Class {iri: "http://uri.interlex.org/tgbugs/uris/readable/NeuronEBM"}) // FIXME
, (start:Ontology)
<-[:apinat:hasGraph]-(graph:Class {iri: ""})
RETURN external

neru model populations

Given an ApiNATOMY model id return the identifiers for the neuron populations that are present in the model.

MATCH (start:Ontology {iri: $model_id})
<-[:isDefinedBy]-(external:Class)
-[:subClassOf*]->(:Class {iri: "http://uri.interlex.org/tgbugs/uris/readable/NeuronEBM"}) // FIXME
RETURN external

neru model populations and references

Given an ApiNATOMY model id return the identifiers for the neuron populations that are present in the model and the identifiers for references that provide supporting evidence. Publications and populations can be distingished by checking whether their meta type field is NamedIndividual or Class.

MATCH (start:Ontology {iri: $model_id})
<-[:isDefinedBy]-(external:Class)
-[:subClassOf*]->(:Class {iri: "http://uri.interlex.org/tgbugs/uris/readable/NeuronEBM"}) // FIXME
,
(external)
-[e:type]->()
RETURN e
UNION
OPTIONAL MATCH (start:Ontology {iri: $model_id})
<-[:isDefinedBy]-(graph:NamedIndividual)
-[:type]->({iri: "https://apinatomy.org/uris/elements/Graph"}) // elements don't have a superclass right now
,
(graph)
-[:apinatomy:references]->(pub)
-[e:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Reference"})
RETURN e

TODO neru regions projected to


TODO neru regions dendrited to

TODO neru regions with processes from

region contains some process part of some neuron population

MATCH (region)
-[:apinatomy:annotates]->(lyph)
<-[:apinatomy:lyphs]-
(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"})
RETURN region

neru projects to

neru neurites to

MATCH (neupop:Class{iri: $neupop_id})
-[:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"})
-[:apinatomy:lyphs]->(end_lyph)
-[t:apinatomy:topology]->(top)
WHERE top.iri IN ["https://apinatomy.org/uris/readable/BAG", "https://apinatomy.org/uris/readable/BAG2"]
WITH neugrp, end_lyph, t
MATCH (type) // FIXME we need more information in the models
<-[x:apinatomy:inheritedOntologyTerms*0..1]-(end_lyph)
-[a:apinatomy:conveys]->(link)
-[b:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
-[c:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph
WITH neugrp, lyph, a,b,c,t,x
MATCH (lyph)
-[e:apinatomy:ontologyTerms]->(region)
RETURN e, a,b,c,t,x

neru population projects to

neru populations projecting to

Given an anatomical region return a list of neuron types that project to that region.

MATCH
(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"})
-[:apinatomy:links]->(link)
-[:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer)
-[:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:ontologyTerms*0..1]->(region:Class{iri: $region_id})
WITH neugrp
OPTIONAL MATCH (neupop:Class)
-[e:apinatomy:annotates*0..1]->(neugrp)
WITH e, neupop, neugrp, link
MATCH (link)
-[:apinatomy:conveyingLyph]->(cl)
-[:apinatomy:inheritedOntologyTerms]->(:Class {iri: $type_id})
WITH e, neupop, neugrp, cl
MATCH (cl)
-[:apinatomy:topology]-(top)
WHERE top.iri IN ["https://apinatomy.org/uris/readable/BAG", "https://apinatomy.org/uris/readable/BAG2"]
RETURN e, neupop, neugrp

neru populations with axons in

Given an anatomical region return a list of neuron types with axons that are located in that region.

MATCH
(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"})
-[:apinatomy:links]->(link)
-[:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer)
-[:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:ontologyTerms*0..1]->(region:Class{iri: $region_id})
WITH neugrp
OPTIONAL MATCH (neupop:Class)
-[e:apinatomy:annotates*0..1]->(neugrp)
WITH e, neupop, neugrp, link
MATCH (link)
-[:apinatomy:conveyingLyph]->(cl)
-[:apinatomy:inheritedOntologyTerms]->(:Class {iri: $type_id})
RETURN e, neupop, neugrp

neru-axons.svg

neru populations dendriting to

Given an anatomical region return a list of neuron types that have dendrite terminals in that region.

MATCH
(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"})
-[:apinatomy:links]->(link)
-[:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer)
-[:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:ontologyTerms*0..1]->(region:Class{iri: $region_id})
WITH neugrp
OPTIONAL MATCH (neupop:Class)
-[e:apinatomy:annotates*0..1]->(neugrp)
WITH e, neupop, neugrp, link
MATCH (link)
-[:apinatomy:conveyingLyph]->(cl)
-[:apinatomy:inheritedOntologyTerms]->(:Class {iri: $type_id})
WITH e, neupop, neugrp, cl
MATCH (cl)
-[:apinatomy:topology]-(top)
WHERE top.iri IN ["https://apinatomy.org/uris/readable/BAG", "https://apinatomy.org/uris/readable/BAG2"]
RETURN e, neupop, neugrp

neru-dendrites-bag.svg

neru populations with dendrites in

Given an anatomical region return a list of neuron types with dendrites that are located in that region.

MATCH
(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"})
-[:apinatomy:links]->(link)
-[:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer)
-[:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:ontologyTerms*0..1]->(region:Class{iri: $region_id})
WITH neugrp
OPTIONAL MATCH (neupop:Class)
-[e:apinatomy:annotates*0..1]->(neugrp)
WITH e, neupop, neugrp, link
MATCH (link)
-[:apinatomy:conveyingLyph]->(cl)
-[:apinatomy:inheritedOntologyTerms]->(:Class {iri: $type_id})
RETURN e, neupop, neugrp

neru-dendrites.svg

TODO neru populations with neurites in

neru populations with processes in

Given an anatomical region return a list of neuron types with processes that are located in that region.

MATCH
(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"})
-[:apinatomy:links]->(link)
-[:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer)
-[:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:ontologyTerms*0..1]->(region:Class{iri: $region_id})
WITH neugrp
OPTIONAL MATCH (neupop:Class)
-[e:apinatomy:annotates*0..1]->(neugrp)
// somas
MATCH
(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"})
-[:apinatomy:lyphs]->(lyph)
-[:apinatomy:internalIn]->()
-[:apinatomy:ontologyTerms*0..1]->(region:Class{iri: $region_id})
WITH neugrp
OPTIONAL MATCH (neupop:Class)
-[e:apinatomy:annotates*0..1]->(neugrp)

TODO Need to expand region_id using the Uberon partonomy to answer more generic queries.

// dendrites and axons
MATCH
(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"})
-[:apinatomy:links]->(link)
-[:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer)
-[:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:ontologyTerms*0..1]->(region:Class{iri: $region_id})
WITH neugrp
OPTIONAL MATCH (neupop:Class)
-[e:apinatomy:annotates*0..1]->(neugrp)
RETURN e, neupop, neugrp

UNION

// somas
MATCH
(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"})
-[:apinatomy:lyphs]->(lyph)
-[:apinatomy:internalIn]->()
-[:apinatomy:ontologyTerms*0..1]->(region:Class{iri: $region_id})
WITH neugrp
OPTIONAL MATCH (neupop:Class)
-[e:apinatomy:annotates*0..1]->(neugrp)
RETURN e, neupop, neugrp

neru-processes.svg

MATCH
(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"})
-[:apinatomy:links]->(link)
-[:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer)
-[:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:ontologyTerms*0..1]->(region:Class{iri: $region_id})
WITH neugrp
OPTIONAL MATCH (neupop:Class)
-[e:apinatomy:annotates*0..1]->(neugrp)
RETURN neupop

UNION

// somas
MATCH
(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"})
-[:apinatomy:lyphs]->(lyph)
-[:apinatomy:internalIn]->()
-[:apinatomy:ontologyTerms*0..1]->(region:Class{iri: $region_id})
WITH neugrp
OPTIONAL MATCH (neupop:Class)
-[e:apinatomy:annotates*0..1]->(neugrp)
RETURN neupop

Helper blocks for more specialized queries.

Filter by process type_id, where type_id could be the ontology identifier for axon or dendrite. The type_id has to match the conventions used in ApiNATOMY models.

MATCH
(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"})
-[:apinatomy:links]->(link)
-[:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer)
-[:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:ontologyTerms*0..1]->(region:Class{iri: $region_id})
WITH neugrp
OPTIONAL MATCH (neupop:Class)
-[e:apinatomy:annotates*0..1]->(neugrp)
WITH e, neupop, neugrp, link
MATCH (link)
-[:apinatomy:conveyingLyph]->(cl)
-[:apinatomy:inheritedOntologyTerms]->(:Class {iri: $type_id})

Filter processes based on their topology. For example, this is used to select processes with bag topology that correspond to axon and dendrite terminals.

MATCH
(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"})
-[:apinatomy:links]->(link)
-[:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer)
-[:apinatomy:layerIn*0..1]->(lyph)
-[:apinatomy:ontologyTerms*0..1]->(region:Class{iri: $region_id})
WITH neugrp
OPTIONAL MATCH (neupop:Class)
-[e:apinatomy:annotates*0..1]->(neugrp)
WITH e, neupop, neugrp, link
MATCH (link)
-[:apinatomy:conveyingLyph]->(cl)
-[:apinatomy:inheritedOntologyTerms]->(:Class {iri: $type_id})
WITH e, neupop, neugrp, cl
MATCH (cl)
-[:apinatomy:topology]-(top)
WHERE top.iri IN ["https://apinatomy.org/uris/readable/BAG", "https://apinatomy.org/uris/readable/BAG2"]
RETURN e, neupop, neugrp

TODO neru populations by phenotype

TODO neru phenotype by population

existing

sparc

  • organParts

    Get the parts list for an organ including nerves and blood vessels

    // depth of 6 captures everything, 5 is too shallow, 40 is WAY too deep
    MATCH path = (start:Class{iri: "${id}"})
    <-[:subClassOf|ilxtr:includedForSPARCUnder|fma:regional_part_of|fma:constitutional_part_of|fma:related_part_of*0..6]-(part)
    <-[:fma:arterial_supply_of|fma:nerve_supply_of|fma:venous_drainage_of|fma:continuous_with*0..1]-(sup)
    <-[:subClassOf|fma:constitutional_part_of|fma:branch|fma:tributary|fma:branch_of*0..1]-(a_bit_more)
    RETURN path
    UNION  // for this query UNION seems to be MUCH faster than using WITH
    MATCH path = (start:Class{iri: "${id}"})
    // this one does not need to be inverted ? except for the INCOMING flag
    <-[:fma:arterial_supply_of|fma:venous_drainage_of]-(vessel)
    <-[:fma:branch|fma:tributary]-(more_vessel)
    <-[:fma:branch|fma:tributary|fma:regional_part]-(even_more_vessel)
    RETURN path
    
  • parcellationArtifacts
    MATCH path = (artifact)
    -[:subClassOf*0..2]->(parent)
    -[:ilxtr:isDefinedInTaxon]->(species)
    WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
    RETURN path
    
  • parcellationArtifacts/{species-id}
    MATCH path = (artifact)
    -[:subClassOf*0..2]->(parent)
    -[:ilxtr:isDefinedInTaxon]->(species:Class{iri: "${species-id}"})
    WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
    RETURN path
    
  • artifactRoots/{artifact-id}
    MATCH path = (root)
    -[:ilxtr:isDefinedBy]->(a)<-[:subClassOf*0..2]
    -(artifact:Class{iri: "${artifact-id}"})
    RETURN path
    
  • artifactLabels/{artifact-id}
    MATCH path = (label)
    -[:subClassOf]->(root)
    -[:ilxtr:isDefinedBy]->(a)<-[:subClassOf*0..2]
    -(artifact:Class{iri: "${artifact-id}"})
    WHERE label.iri <> "http://www.w3.org/2002/07/owl#Nothing"
    RETURN path
    
  • rootLabels/{root-id}
    MATCH (label)-[:subClassOf]->(root:Class{iri: "${root-id}"})
    , path = (label)-[relation*0..1]-(maybe)
    WHERE NONE (r in relation WHERE type(r) IN ["isDefinedBy", "subClassOf", "filler"])
          AND NOT (label.iri =~ ".*_:.*") AND NOT (maybe.iri =~ ".*_:.*")
          AND label.iri <> "http://www.w3.org/2002/07/owl#Nothing"
    RETURN path
    
  • parcellationRoots
    MATCH path = (artifact)
    -[:subClassOf*0..2]->(parent)
    -[:ilxtr:isDefinedInTaxon]->(species)
    WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
    RETURN path
    UNION
    MATCH path = (root)
    -[:ilxtr:isDefinedBy]->(artifact)
    -[:subClassOf*0..2]->(parent)
    -[:ilxtr:isDefinedInTaxon]->(species)
    RETURN path
    
  • parcellationRoots/{species-id}
    MATCH path = (artifact)
    -[:subClassOf*0..2]->(parent)
    -[:ilxtr:isDefinedInTaxon]->(species:Class{iri: "${species-id}"})
    WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
    return path
    UNION
    MATCH path = (root)
    -[:ilxtr:isDefinedBy]->(artifact)
    -[:subClassOf*0..2]->(parent)
    -[:ilxtr:isDefinedInTaxon]->(species:Class{iri: "${species-id}"})
    RETURN path
    
  • parcellationRoots/{species-id}/{region-id}
    MATCH
    (region:Class{iri: "${region-id}"})
    <-[:ilxtr:isDefinedInRegion]-
    (parent)
    -[:ilxtr:isDefinedInTaxon]->
    (species:Class{iri: "${species-id}"})
    WITH parent
    MATCH path = (artifact)
    -[:subClassOf*0..2]->(parent)
    WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
    RETURN path
    UNION
    MATCH
    (region:Class{iri: "${region-id}"})
    <-[:ilxtr:isDefinedInRegion]-
    (parent)
    -[:ilxtr:isDefinedInTaxon]->
    (species:Class{iri: "${species-id}"})
    WITH parent
    MATCH path = (root)
    -[:ilxtr:isDefinedBy]->(artifact)
    -[:subClassOf*0..2]->(parent)
    RETURN path
    
  • parcellationRootsFMA/{species-id}/{fma-id}
    MATCH (fma:Class{iri: "${fma-id}"})
    WITH "FMA:" + toString(fma.`http://purl.org/sig/ont/fma/FMAID`) AS curie
    MATCH (region)
    -[:subClassOf*]->(start:Class{iri: "http://purl.obolibrary.org/obo/UBERON_0001062"})
    WHERE any(x IN
              region.`http://www.geneontology.org/formats/oboInOwl#hasDbXref`
              WHERE x =~ curie)
    WITH region
    MATCH
    (region)
    <-[:ilxtr:isDefinedInRegion]-
    (parent)
    -[:ilxtr:isDefinedInTaxon]->
    (species:Class{iri: "${species-id}"})
    WITH parent
    MATCH path = (artifact)
    -[:subClassOf*0..2]->(parent)
    WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
    RETURN path
    UNION
    MATCH (fma:Class{iri: "${fma-id}"})
    WITH "FMA:" + toString(fma.`http://purl.org/sig/ont/fma/FMAID`) AS curie
    MATCH (region)
    -[:subClassOf*]->(start:Class{iri: "http://purl.obolibrary.org/obo/UBERON_0001062"})
    WHERE any(x IN
              region.`http://www.geneontology.org/formats/oboInOwl#hasDbXref`
              WHERE x =~ curie)
    WITH region
    MATCH
    (region)
    <-[:ilxtr:isDefinedInRegion]-
    (parent)
    -[:ilxtr:isDefinedInTaxon]->
    (species:Class{iri: "${species-id}"})
    WITH parent
    MATCH path = (root)
    -[:ilxtr:isDefinedBy]->(artifact)
    -[:subClassOf*0..2]->(parent)
    RETURN path
    
  • parcellationGraph
    MATCH path = (artifact)
    -[:subClassOf*0..2]->(parent)
    -[:ilxtr:isDefinedInTaxon]->(species)
    WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
    return path
    UNION
    MATCH path = (maybe)
    -[relation*0..1]-(label)
    -[:subClassOf]->(root)
    -[:ilxtr:isDefinedBy]->(artifact)
    -[:subClassOf*0..2]->(parent)
    -[:ilxtr:isDefinedInTaxon]->(species)
    WHERE NONE (r in relation WHERE type(r) IN ["isDefinedBy", "subClassOf", "filler"])
          AND NOT (label.iri =~ ".*_:.*") AND NOT (maybe.iri =~ ".*_:.*")
          AND label.iri <> "http://www.w3.org/2002/07/owl#Nothing"
    RETURN path
    
  • organList
    MATCH (n)
    WHERE n.iri IN [
            "http://purl.org/sig/ont/fma/fma7195",  // lung
            "http://purl.org/sig/ont/fma/fma7088",  // heart
            "http://purl.org/sig/ont/fma/fma7197",  // liver
            "http://purl.org/sig/ont/fma/fma7198",  // pancreas
            "http://purl.org/sig/ont/fma/fma7203",  // kidney
            "http://purl.org/sig/ont/fma/fma7148",  // stomach
            "http://purl.org/sig/ont/fma/fma7196",  // spleen
            "http://purl.org/sig/ont/fma/fma14543", // colon
            "http://purl.org/sig/ont/fma/fma7201",  // large intestine
            "http://purl.org/sig/ont/fma/fma7200",  // small intestine
            "http://purl.org/sig/ont/fma/fma7199",  // intestine
            "http://purl.org/sig/ont/fma/fma15900", // urinary bladder
            "http://purl.org/sig/ont/fma/fma45659", // lower urinary tract
            "http://purl.org/sig/ont/fma/fma7157",  // nervous system
            "http://purl.org/sig/ont/fma/fma9903",  // peripheral nervous system
            "http://purl.org/sig/ont/fma/fma9906",  // sympathetic nervous system
            "http://purl.org/sig/ont/fma/fma7647",  // spinal cord
            "http://purl.org/sig/ont/fma/fma50801", // brain
            "http://purl.org/sig/ont/fma/fma5889",  // autonomic ganglion
            "http://purl.org/sig/ont/fma/fma19667", // urethra
            "http://purl.org/sig/ont/fma/fma7131"   // esophagus
            ]
    RETURN n
    
  • speciesList
    MATCH (n)
    WHERE n.iri IN [
            "http://purl.obolibrary.org/obo/NCBITaxon_9378",   // Suncus murinus
            "http://purl.obolibrary.org/obo/NCBITaxon_9606",   // Homo sapiens
            "http://purl.obolibrary.org/obo/NCBITaxon_9685",   // Felis catus
            "http://purl.obolibrary.org/obo/NCBITaxon_9823",   // Sus scrofa
            "http://purl.obolibrary.org/obo/NCBITaxon_10090",  // Mus musculus
            "http://purl.obolibrary.org/obo/NCBITaxon_10116"   // Rattus norvegicus
            ]
    RETURN n
    
  • sparc community terms
    MATCH
    (t)-[:isDefinedBy]->(graph:Ontology{iri: "http://uri.interlex.org/sparc/ontologies/community-terms"})
    RETURN t
    
  • anatomyPhenotypes

    Return phenotypes that inhere in some part of an anatomical entity.

    MATCH (anatomical_entity:Class{iri: $id})
    <-[part_of:subClassOf|BFO:0000050|RO:0002433*0..40]-(subclass_part_ctmo)
    <-[inheres_in:RO:0000052|RO:0002314!]-(intersection_of_quality_and_anatomy) // inheres in (anon intersection of)
    <-[has_process_part:BFO:0000051]-() // has part (process)
    <-[:subClassOf*0..]-(phenotype) // phenotype and all subclasses
    
    WHERE
    // filter out partOf/hasPart cases that induce circularity
    ALL(e IN part_of WHERE NOT EXISTS(e.owlType) OR e.owlType = "subClassOf" OR e.owlType = "operand")
    RETURN phenotype
    

shortestSimple

MATCH (start:Class{iri: '${start_id}'})
WITH start
MATCH (end:Class{iri: '${end_id}'})
WITH start, end
MATCH path = shortestPath((start)-[:${relationship}*..${max_depth}]->(end))
RETURN path

neurons

  • connectivity
    MATCH (blank)-
    [entrytype:ilxtr:hasSomaLocatedIn|ilxtr:hasAxonLocatedIn|ilxtr:hasDendriteLocatedIn|ilxtr:hasPresynapticTerminalsIn]
    ->(location:Class{iri: '${start_id}'})
    WITH location, entrytype, blank
    MATCH (phenotype)<-[predicate]-(blank)<-[:equivalentClass]-(neuron)
    WHERE NOT (phenotype.iri =~ ".*_:.*")
    // RETURN phenotype, (phenotype)-[predicate]-(neuron) as e
    // WITH location, predicate, phenotype, neuron
    RETURN location, entrytype, neuron, predicate, phenotype
    
  • connectedRegions
    MATCH (blank)-
    [entrytype:ilxtr:hasSomaLocatedIn|ilxtr:hasAxonLocatedIn|ilxtr:hasDendriteLocatedIn|ilxtr:hasPresynapticTerminalsIn]
    ->(location:Class{iri: '${start_id}'})
    WITH entrytype, blank
    MATCH (phenotype)<-[:${target_predicate}]-(blank)
    // WHERE NOT (phenotype.iri =~ ".*_:.*")
    RETURN phenotype
    

apinat

  • bundles/{start-id}
    MATCH path1 = (start:Class{iri: "${start-id}"})
    -[:apinatomy:annotates]->(start_housing)
    -[:apinatomy:subtypes*0..1]->()
    -[:apinatomy:clones*0..1]->(layer_or_end)
    -[:apinatomy:layers*0..1]->()
    -[:apinatomy:bundles]->(linkStart)
    -[:apinatomy:prevChainEndLevels|apinatomy:prev|apinatomy:source*1..]->(link)
    -[:apinatomy:targetOf|apinatomy:sourceOf]->(linkSoma)  // axon or dendrite root
    -[:apinatomy:conveyingLyph]->()
    -[:apinatomy:supertype*0..1]->(soma:NamedIndividual)
    -[:apinatomy:ontologyTerms]->(c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
    WITH path1, link
    OPTIONAL MATCH path2 = (link)
    -[:apinatomy:fasciculatesIn|apinatomy:endsIn]->(layer_or_end)
    -[:apinatomy:layerIn*0..1]->(end)
    -[:apinatomy:ontologyTerms]->(external)
    RETURN path1, path2
    
  • old-bundles/{start-id}
    MATCH path1 = (start:Class{iri: '${start-id}'})
    -[:apinatomy:annotates]->(start_housing)
    -[:apinatomy:bundlesChains]->(chain)
    -[:apinatomy:root]->(root)
    -[:apinatomy:internalIn]->(layer_or_end)  # this hits a cycle back to start_housing
    -[:apinatomy:cloneOf*0..1]->()
    -[:apinatomy:supertype*0..1]->()
    -[:apinatomy:ontologyTerms]->(layer_or_end_external)
    WITH path1, root, layer_or_end AS layer
    OPTIONAL MATCH path2 = (layer)
    -[:apinatomy:layerIn]->(end_housing)
    -[:apinatomy:ontologyTerms]->(end_housing_external)
    WITH path1, path2, root
    MATCH path3 = (root) // in the layer case this hits an additional lyph
    <-[:apinatomy:target|apinatomy:source]-(link)
    <-[:apinatomy:conveys]-(soma)
    <-[:apinatomy:annotates]-(soma_NLX)
    RETURN path1, path2, path3
    
  • somas
    MATCH (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
    -[:apinatomy:annotates]->(soma:NamedIndividual)
    RETURN soma
    
  • housing-lyphs
    MATCH path = (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
    -[:apinatomy:annotates]->(soma:NamedIndividual)  // soma lyph
    -[:apinatomy:conveys]->(somaLink)                // link connecting soma to axon and dendrite
    -[:apinatomy:target|apinatomy:source]->(root)    // axon or dendrite root
    -[:apinatomy:controlNodes|apinatomy:rootOf*1..2]->(chain)                    // axon or dendrite tree
    -[:apinatomy:housingLyphs]->(housing)            // list of lyphs housing the trees
    -[:apinatomy:ontologyTerms*0..1]->(external)          // external ids for the housing lyphs
    WHERE soma.`https://apinatomy.org/uris/readable/generated` IS NULL
    RETURN path
    
  • housing-lyphs/{start-id}
    MATCH path1 = (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
    -[:apinatomy:annotates]->(soma:NamedIndividual)  // soma lyph
    -[:apinatomy:conveys]->(somaLink)                // link connecting soma to axon and dendrite
    -[:apinatomy:target|apinatomy:source]->(root)    // axon or dendrite root
    -[:apinatomy:internalIn]->(layer_or_end)
    -[:apinatomy:cloneOf*0..1]->()
    -[:apinatomy:supertype*0..1]->()
    -[:apinatomy:ontologyTerms]->(layer_or_end_external:Class{iri: '${start-id}'})
    WHERE soma.`https://apinatomy.org/uris/readable/generated` IS NULL
    WITH path1, root
    MATCH path2 = (root)
    -[:apinatomy:controlNodes|apinatomy:rootOf*1..2]->(chain)                    // axon or dendrite tree
    -[:apinatomy:housingLyphs]->(housing)            // list of lyphs housing the trees
    -[:apinatomy:ontologyTerms*0..1]->(external)          // external ids for the housing lyphs
    RETURN path1, path2
    
    UNION
    
    MATCH path1 = (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
    -[:apinatomy:annotates]->(soma:NamedIndividual)  // soma lyph
    -[:apinatomy:conveys]->(somaLink)                // link connecting soma to axon and dendrite
    -[:apinatomy:target|apinatomy:source]->(root)    // axon or dendrite root
    -[:apinatomy:internalIn]->(layer)
    -[:apinatomy:cloneOf*0..1]->()
    -[:apinatomy:supertype*0..1]->()
    -[:apinatomy:layerIn]->(end_housing)
    -[:apinatomy:ontologyTerms]->(end_housing_external:Class{iri: '${start-id}'})
    WHERE soma.`https://apinatomy.org/uris/readable/generated` IS NULL
    WITH path1, root
    MATCH path2 = (root)
    -[:apinatomy:rootOf]->(chain)                    // axon or dendrite tree
    -[:apinatomy:housingLyphs]->(housing)            // list of lyphs housing the trees
    -[:apinatomy:ontologyTerms*0..1]->(external)          // external ids for the housing lyphs
    RETURN path1, path2
    
  • soma-processes
    MATCH path1 = (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
    -[:apinatomy:annotates]->(soma:NamedIndividual)    // soma lyph
    -[:apinatomy:conveys]->(linkSoma)                  // link connecting soma to axon and dendrite
    -[:apinatomy:target|apinatomy:source]->(nodeRoot)  // axon or dendrite root
    -[:apinatomy:sourceOf|apinatomy:nextChainStartLevels|apinatomy:next*1..]->(link)  // sourceOf is first and only once
    -[:apinatomy:fasciculatesIn|apinatomy:endsIn]->(layer_or_end)
    -[:apinatomy:cloneOf*0..1]->()
    -[:apinatomy:supertype*0..1]->()
    -[:apinatomy:ontologyTerms]->(external)
    WHERE soma.`https://apinatomy.org/uris/readable/generated` IS NULL
    WITH path1, nodeRoot, layer_or_end AS layer
    OPTIONAL MATCH path2 = (layer)  // if we were in a layer, get the containing lyph as well
    -[:apinatomy:layerIn]->(end_housing)
    -[:apinatomy:ontologyTerms]->(end_housing_external)
    WITH path1, path2, nodeRoot
    MATCH path3 = (nodeRoot)        // extract chain for axon vs dendrite
    -[:apinatomy:rootOf]->(chain)
    RETURN path1, path2, path3
    

    A sparql version of the query (work in progress).

    SELECT DISTINCT
    ?c
    ?soma
    ?linkSoma
    ?nodeRoot
    ?link
    ?layer_or_end
    ?cloned
    ?supertype
    ?external_st
    ?external_loe
    ?external_end_housing
    WHERE
    {
      VALUES ?c { NLX:154731 }
      ?c rdf:type owl:Class .
      ?c apinatomy:annotates ?soma .
      ?soma rdf:type owl:NamedIndividual .
      ?soma apinatomy:conveys ?linkSoma .
      ?linkSoma apinatomy:target | apinatomy:source ?nodeRoot .
      ?nodeRoot apinatomy:rootOf ?chain .
      ?nodeRoot apinatomy:sourceOf | apinatomy:nextChainStartLevels | apinatomy:next+ ?link .
      ?link apinatomy:fasciculatesIn | apinatomy:endsIn ?layer_or_end .
      optional { ?layer_or_end apinatomy:cloneOf* ?cloned . } # optional
      optional { ?cloned apinatomy:supertype* ?supertype . } # optional
    
      ?supertype apinatomy:ontologyTerms ?external_st .
      ?layer_or_end apinatomy:ontologyTerms ?external_loe .
    
      optional { ?layer_or_end apinatomy:layerIn ?end_housing . } # optional
      optional { ?end_housing apinatomy:ontologyTerms ?external_end_housing . } # optional
    }
    limit ?limit
    
  • soma-processes/{start-id}
    MATCH path1 = (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
    -[:apinatomy:annotates]->(soma:NamedIndividual)    // soma lyph
    -[:apinatomy:conveys]->(linkSoma)                  // link connecting soma to axon and dendrite
    -[:apinatomy:target|apinatomy:source]->(nodeRoot)  // axon or dendrite root
    -[:apinatomy:internalIn]->(layer_or_end)
    -[:apinatomy:cloneOf*0..1]->()
    -[:apinatomy:supertype*0..1]->()
    -[:apinatomy:layerIn*0..1]->(layerSoma)  // don't need to see both layer and housing for soma
    -[:apinatomy:ontologyTerms]->(externalEndSoma:Class{iri: '${start-id}'})
    WHERE soma.`https://apinatomy.org/uris/readable/generated` IS NULL
    WITH path1, nodeRoot
    MATCH path3 = (chain)
    <-[:apinatomy:rootOf]-(nodeRoot)
    -[:apinatomy:sourceOf|apinatomy:nextChainStartLevels|apinatomy:next*1..]->(link)
    -[:apinatomy:fasciculatesIn|apinatomy:endsIn]->(layer_or_end)
    -[:apinatomy:cloneOf*0..1]->()
    -[:apinatomy:supertype*0..1]->()
    -[:apinatomy:ontologyTerms]->(external)
    WITH path1, path3, nodeRoot, layer_or_end AS layer
    OPTIONAL MATCH path2 = (layer)  // if we were in a layer, get the containing lyph as well
    -[:apinatomy:layerIn]->(end_housing)
    -[:apinatomy:ontologyTerms]->(end_housing_external)
    RETURN path1, path2, path3
    
  • weird-soma-processes/{process-id}
    MATCH path1 = (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
    -[:apinatomy:annotates]->(soma:NamedIndividual)    // soma lyph
    -[:apinatomy:conveys]->(linkSoma)                  // link connecting soma to axon and dendrite
    -[:apinatomy:target|apinatomy:source]->(nodeRoot)  // axon or dendrite root
    -[:apinatomy:sourceOf|apinatomy:nextChainStartLevels|apinatomy:next*1..]->(link)  // sourceOf is first and only once
    -[:apinatomy:fasciculatesIn|apinatomy:endsIn]->(layer_or_end)
    -[:apinatomy:cloneOf*0..1]->()
    -[:apinatomy:supertype*0..1]->()
    -[:apinatomy:ontologyTerms]->(external:Class{iri: '${process-id}'})
    WHERE soma.`https://apinatomy.org/uris/readable/generated` IS NULL
    WITH path1, nodeRoot, layer_or_end AS layer
    OPTIONAL MATCH path2 = (layer)  // if we were in a layer, get the containing lyph as well
    -[:apinatomy:layerIn]->(end_housing)
    -[:apinatomy:ontologyTerms]->(end_housing_external)
    WITH path1, path2, nodeRoot
    MATCH path3 = (nodeRoot)        // extract chain for axon vs dendrite
    -[:apinatomy:rootOf]->(chain)
    RETURN path1, path2, path3
    
    UNION
    
    MATCH path1 = (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
    -[:apinatomy:annotates]->(soma:NamedIndividual)    // soma lyph
    -[:apinatomy:conveys]->(linkSoma)                  // link connecting soma to axon and dendrite
    -[:apinatomy:target|apinatomy:source]->(nodeRoot)  // axon or dendrite root
    -[:apinatomy:sourceOf|apinatomy:nextChainStartLevels|apinatomy:next*1..]->(link)  // sourceOf is first and only once
    -[:apinatomy:fasciculatesIn|apinatomy:endsIn]->(layer_or_end)
    -[:apinatomy:cloneOf*0..1]->()
    -[:apinatomy:supertype*0..1]->()
    -[:apinatomy:ontologyTerms]->(external)
    WHERE soma.`https://apinatomy.org/uris/readable/generated` IS NULL
    WITH path1, nodeRoot, layer_or_end AS layer
    MATCH path2 = (layer)  // if we were in a layer, get the containing lyph as well
    -[:apinatomy:layerIn]->(end_housing)
    -[:apinatomy:ontologyTerms]->(end_housing_external:Class{iri: '${process-id}'})
    WITH path1, path2, nodeRoot
    MATCH path3 = (nodeRoot)        // extract chain for axon vs dendrite
    -[:apinatomy:rootOf]->(chain)
    RETURN path1, path2, path3
    

yaml file

Example template.

cypherResources:
  - path:
    operations: &name-ops
      - summary: <<nonl(desc-name())>>
        parameters:
          - name: some-id
            description: does something
            paramType:
    query: &name |
      <<block-name>>
cypherResources:
  - path: /dynamic/prod/sparc/anatomyPhenotypes/{id}
    operations: &anatomyPhenotypes-ops
      - summary: nil
        parameters:
          - name: id
            description: uberon id of the anatomical entity to search from
            paramType: path
    query: &anatomyPhenotypes |
      MATCH (anatomical_entity:Class{iri: $id})
      <-[part_of:subClassOf|BFO:0000050|RO:0002433*0..40]-(subclass_part_ctmo)
      <-[inheres_in:RO:0000052|RO:0002314!]-(intersection_of_quality_and_anatomy) // inheres in (anon intersection of)
      <-[has_process_part:BFO:0000051]-() // has part (process)
      <-[:subClassOf*0..]-(phenotype) // phenotype and all subclasses

      WHERE
      // filter out partOf/hasPart cases that induce circularity
      ALL(e IN part_of WHERE NOT EXISTS(e.owlType) OR e.owlType = "subClassOf" OR e.owlType = "operand")
      RETURN phenotype

  - path: /dynamic/test/sparc/anatomyPhenotypes/{id}
    operations: *anatomyPhenotypes-ops
    query: *anatomyPhenotypes

  - path: /dynamic/prod/sparc/organParts/{id}
    operations: &organParts-ops
      - summary: nil
        parameters:
         - name: id
           description: ontology id of the organ
           paramType: path
    query: &organParts |
      // depth of 6 captures everything, 5 is too shallow, 40 is WAY too deep
      MATCH path = (start:Class{iri: "${id}"})
      <-[:subClassOf|ilxtr:includedForSPARCUnder|fma:regional_part_of|fma:constitutional_part_of|fma:related_part_of*0..6]-(part)
      <-[:fma:arterial_supply_of|fma:nerve_supply_of|fma:venous_drainage_of|fma:continuous_with*0..1]-(sup)
      <-[:subClassOf|fma:constitutional_part_of|fma:branch|fma:tributary|fma:branch_of*0..1]-(a_bit_more)
      RETURN path
      UNION  // for this query UNION seems to be MUCH faster than using WITH
      MATCH path = (start:Class{iri: "${id}"})
      // this one does not need to be inverted ? except for the INCOMING flag
      <-[:fma:arterial_supply_of|fma:venous_drainage_of]-(vessel)
      <-[:fma:branch|fma:tributary]-(more_vessel)
      <-[:fma:branch|fma:tributary|fma:regional_part]-(even_more_vessel)
      RETURN path

  - path: /dynamic/test/sparc/organParts/{id}
    operations: *organParts-ops
    query: *organParts

  - path: /dynamic/prod/sparc/parcellationArtifacts
    operations:
      - summary: Get the graph of all parcellation artifacts for all species
    query: |
      MATCH path = (artifact)
      -[:subClassOf*0..2]->(parent)
      -[:ilxtr:isDefinedInTaxon]->(species)
      WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
      RETURN path

  - path: /dynamic/prod/sparc/parcellationArtifacts/{species-id}
    operations:
      - summary: Get the graph of all parcellation artifacts for a single species
        parameters:
         - name: species-id
           description: ontology id of the species
           paramType: path
    query: |
      MATCH path = (artifact)
      -[:subClassOf*0..2]->(parent)
      -[:ilxtr:isDefinedInTaxon]->(species:Class{iri: "${species-id}"})
      WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
      RETURN path

  - path: /dynamic/prod/sparc/artifactRoots/{artifact-id}
    operations:
      - summary: Get the graph of all parcellation label roots for a single artifact WARNING this can return no results
        parameters:
         - name: artifact-id
           description: ontology id of the parcellation artifact
           paramType: path
    query: |
      MATCH path = (root)
      -[:ilxtr:isDefinedBy]->(a)<-[:subClassOf*0..2]
      -(artifact:Class{iri: "${artifact-id}"})
      RETURN path

  - path: /dynamic/prod/sparc/artifactLabels/{artifact-id}
    operations:
      - summary: Get the graph of all parcellation labels for a single artifact WARNING this can return no results
        parameters:
         - name: artifact-id
           description: ontology id of the parcellation artifact
           paramType: path
    query: |
      MATCH path = (label)
      -[:subClassOf]->(root)
      -[:ilxtr:isDefinedBy]->(a)<-[:subClassOf*0..2]
      -(artifact:Class{iri: "${artifact-id}"})
      WHERE label.iri <> "http://www.w3.org/2002/07/owl#Nothing"
      RETURN path

  - path: /dynamic/prod/sparc/rootLabels/{root-id}
    operations: &rootLabes-ops
      - summary: Get the list of all parcellation labels for a single label root
        parameters:
         - name: root-id
           description: ontology id of the parcellation label root
           paramType: path
    query: &rootLabels |
      MATCH (label)-[:subClassOf]->(root:Class{iri: "${root-id}"})
      , path = (label)-[relation*0..1]-(maybe)
      WHERE NONE (r in relation WHERE type(r) IN ["isDefinedBy", "subClassOf", "filler"])
            AND NOT (label.iri =~ ".*_:.*") AND NOT (maybe.iri =~ ".*_:.*")
            AND label.iri <> "http://www.w3.org/2002/07/owl#Nothing"
      RETURN path

  - path: /dynamic/test/sparc/rootLabels/{root-id}
    operations: *rootLabes-ops
    query: *rootLabels

  - path: /dynamic/prod/sparc/parcellationRoots
    operations:
      - summary: Get the graph of all parcellation label roots for all species
    query: |
      MATCH path = (artifact)
      -[:subClassOf*0..2]->(parent)
      -[:ilxtr:isDefinedInTaxon]->(species)
      WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
      RETURN path
      UNION
      MATCH path = (root)
      -[:ilxtr:isDefinedBy]->(artifact)
      -[:subClassOf*0..2]->(parent)
      -[:ilxtr:isDefinedInTaxon]->(species)
      RETURN path

  - path: /dynamic/prod/sparc/parcellationRoots/{species-id}
    operations:
      - summary: Get the graph of all parcellation label roots for a single species
        parameters:
         - name: species-id
           description: ontology id of the species
           paramType: path
    query: |
      MATCH path = (artifact)
      -[:subClassOf*0..2]->(parent)
      -[:ilxtr:isDefinedInTaxon]->(species:Class{iri: "${species-id}"})
      WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
      return path
      UNION
      MATCH path = (root)
      -[:ilxtr:isDefinedBy]->(artifact)
      -[:subClassOf*0..2]->(parent)
      -[:ilxtr:isDefinedInTaxon]->(species:Class{iri: "${species-id}"})
      RETURN path

  - path: /dynamic/prod/sparc/parcellationRoots/{species-id}/{region-id}
    operations:
      - summary: Get the graph of all parcellation label roots for a single species and anatomical region
        parameters:
         - name: species-id
           description: ontology id of the species
           paramType: path
         - name: region-id
           description: ontology id of the anatomical region
           paramType: path
    query: |
      MATCH
      (region:Class{iri: "${region-id}"})
      <-[:ilxtr:isDefinedInRegion]-
      (parent)
      -[:ilxtr:isDefinedInTaxon]->
      (species:Class{iri: "${species-id}"})
      WITH parent
      MATCH path = (artifact)
      -[:subClassOf*0..2]->(parent)
      WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
      RETURN path
      UNION
      MATCH
      (region:Class{iri: "${region-id}"})
      <-[:ilxtr:isDefinedInRegion]-
      (parent)
      -[:ilxtr:isDefinedInTaxon]->
      (species:Class{iri: "${species-id}"})
      WITH parent
      MATCH path = (root)
      -[:ilxtr:isDefinedBy]->(artifact)
      -[:subClassOf*0..2]->(parent)
      RETURN path

  - path: /dynamic/prod/sparc/parcellationRootsFMA/{species-id}/{fma-id}
    operations: &parcellationRootsFMA-ops
      - summary: Get the graph of all parcellation label roots for a single species and anatomical region
        parameters:
         - name: species-id
           description: ontology id of the species
           paramType: path
         - name: fma-id
           description: ontology id of the anatomical region
           paramType: path
    query: &parcellationRootsFMA |
      MATCH (fma:Class{iri: "${fma-id}"})
      WITH "FMA:" + toString(fma.`http://purl.org/sig/ont/fma/FMAID`) AS curie
      MATCH (region)
      -[:subClassOf*]->(start:Class{iri: "http://purl.obolibrary.org/obo/UBERON_0001062"})
      WHERE any(x IN
                region.`http://www.geneontology.org/formats/oboInOwl#hasDbXref`
                WHERE x =~ curie)
      WITH region
      MATCH
      (region)
      <-[:ilxtr:isDefinedInRegion]-
      (parent)
      -[:ilxtr:isDefinedInTaxon]->
      (species:Class{iri: "${species-id}"})
      WITH parent
      MATCH path = (artifact)
      -[:subClassOf*0..2]->(parent)
      WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
      RETURN path
      UNION
      MATCH (fma:Class{iri: "${fma-id}"})
      WITH "FMA:" + toString(fma.`http://purl.org/sig/ont/fma/FMAID`) AS curie
      MATCH (region)
      -[:subClassOf*]->(start:Class{iri: "http://purl.obolibrary.org/obo/UBERON_0001062"})
      WHERE any(x IN
                region.`http://www.geneontology.org/formats/oboInOwl#hasDbXref`
                WHERE x =~ curie)
      WITH region
      MATCH
      (region)
      <-[:ilxtr:isDefinedInRegion]-
      (parent)
      -[:ilxtr:isDefinedInTaxon]->
      (species:Class{iri: "${species-id}"})
      WITH parent
      MATCH path = (root)
      -[:ilxtr:isDefinedBy]->(artifact)
      -[:subClassOf*0..2]->(parent)
      RETURN path

  - path: /dynamic/test/sparc/parcellationRootsFMA/{species-id}/{fma-id}
    operations: *parcellationRootsFMA-ops
    query: *parcellationRootsFMA

  - path: /dynamic/prod/sparc/parcellationGraph
    operations:
      - summary: Get the graph of all parcellation labels for all species
    query: |
      MATCH path = (artifact)
      -[:subClassOf*0..2]->(parent)
      -[:ilxtr:isDefinedInTaxon]->(species)
      WHERE artifact.iri <> "http://www.w3.org/2002/07/owl#Nothing"
      return path
      UNION
      MATCH path = (maybe)
      -[relation*0..1]-(label)
      -[:subClassOf]->(root)
      -[:ilxtr:isDefinedBy]->(artifact)
      -[:subClassOf*0..2]->(parent)
      -[:ilxtr:isDefinedInTaxon]->(species)
      WHERE NONE (r in relation WHERE type(r) IN ["isDefinedBy", "subClassOf", "filler"])
            AND NOT (label.iri =~ ".*_:.*") AND NOT (maybe.iri =~ ".*_:.*")
            AND label.iri <> "http://www.w3.org/2002/07/owl#Nothing"
      RETURN path

  - path: /dynamic/prod/sparc/organList
    operations: &organList-ops
      - summary: Get the list of all FMA organ identifiers relevant to SPARC
    query: &organList |
      MATCH (n)
      WHERE n.iri IN [
              "http://purl.org/sig/ont/fma/fma7195",  // lung
              "http://purl.org/sig/ont/fma/fma7088",  // heart
              "http://purl.org/sig/ont/fma/fma7197",  // liver
              "http://purl.org/sig/ont/fma/fma7198",  // pancreas
              "http://purl.org/sig/ont/fma/fma7203",  // kidney
              "http://purl.org/sig/ont/fma/fma7148",  // stomach
              "http://purl.org/sig/ont/fma/fma7196",  // spleen
              "http://purl.org/sig/ont/fma/fma14543", // colon
              "http://purl.org/sig/ont/fma/fma7201",  // large intestine
              "http://purl.org/sig/ont/fma/fma7200",  // small intestine
              "http://purl.org/sig/ont/fma/fma7199",  // intestine
              "http://purl.org/sig/ont/fma/fma15900", // urinary bladder
              "http://purl.org/sig/ont/fma/fma45659", // lower urinary tract
              "http://purl.org/sig/ont/fma/fma7157",  // nervous system
              "http://purl.org/sig/ont/fma/fma9903",  // peripheral nervous system
              "http://purl.org/sig/ont/fma/fma9906",  // sympathetic nervous system
              "http://purl.org/sig/ont/fma/fma7647",  // spinal cord
              "http://purl.org/sig/ont/fma/fma50801", // brain
              "http://purl.org/sig/ont/fma/fma5889",  // autonomic ganglion
              "http://purl.org/sig/ont/fma/fma19667", // urethra
              "http://purl.org/sig/ont/fma/fma7131"   // esophagus
              ]
      RETURN n

  - path: /dynamic/test/sparc/organList
    operations: *organList-ops
    query: *organList

  - path: /dynamic/prod/sparc/speciesList
    operations: &speciesList-ops
      - summary: Get the list of all NCBITaxon species identifiers relevant to SPARC
    query: &speciesList |
      MATCH (n)
      WHERE n.iri IN [
              "http://purl.obolibrary.org/obo/NCBITaxon_9378",   // Suncus murinus
              "http://purl.obolibrary.org/obo/NCBITaxon_9606",   // Homo sapiens
              "http://purl.obolibrary.org/obo/NCBITaxon_9685",   // Felis catus
              "http://purl.obolibrary.org/obo/NCBITaxon_9823",   // Sus scrofa
              "http://purl.obolibrary.org/obo/NCBITaxon_10090",  // Mus musculus
              "http://purl.obolibrary.org/obo/NCBITaxon_10116"   // Rattus norvegicus
              ]
      RETURN n

  - path: /dynamic/test/sparc/speciesList
    operations: *speciesList-ops
    query: *speciesList

  - path: /dynamic/shortestSimple
    query: |
      MATCH (start:Class{iri: '${start_id}'})
      WITH start
      MATCH (end:Class{iri: '${end_id}'})
      WITH start, end
      MATCH path = shortestPath((start)-[:${relationship}*..${max_depth}]->(end))
      RETURN path
    operations:
      - summary: Get the shortest path between two IDs
        parameters:
          - name: start_id
            description: The starting node (ex UBERON:0005751)
            paramType: query
          - name: end_id
            description: The ending node (ex UBERON:0001255)
            paramType: query
          - name: max_depth
            description: the maximum depth to traverse
            paramType: query
          - name: relationship
            description: The property to traverse (ex subClassOf or subClassOf|partOf|isA)
            paramType: query
            required: false

  - path: /dynamic/neurons/connectivity
    query: |
      MATCH (blank)-
      [entrytype:ilxtr:hasSomaLocatedIn|ilxtr:hasAxonLocatedIn|ilxtr:hasDendriteLocatedIn|ilxtr:hasPresynapticTerminalsIn]
      ->(location:Class{iri: '${start_id}'})
      WITH location, entrytype, blank
      MATCH (phenotype)<-[predicate]-(blank)<-[:equivalentClass]-(neuron)
      WHERE NOT (phenotype.iri =~ ".*_:.*")
      // RETURN phenotype, (phenotype)-[predicate]-(neuron) as e
      // WITH location, predicate, phenotype, neuron
      RETURN location, entrytype, neuron, predicate, phenotype
    operations:
      - summary: Get connected anatomical regions by neuron type
        parameters:
          - name: start_id
            description: The starting location (eg UBERON:0001759)
            paramType: query

  - path: /dynamic/neurons/connectedRegions
    query: |
      MATCH (blank)-
      [entrytype:ilxtr:hasSomaLocatedIn|ilxtr:hasAxonLocatedIn|ilxtr:hasDendriteLocatedIn|ilxtr:hasPresynapticTerminalsIn]
      ->(location:Class{iri: '${start_id}'})
      WITH entrytype, blank
      MATCH (phenotype)<-[:${target_predicate}]-(blank)
      // WHERE NOT (phenotype.iri =~ ".*_:.*")
      RETURN phenotype
    operations:
      - summary: Get connected anatomical regions by starting location and target relationship
        parameters:
          - name: start_id
            description: The starting location (eg UBERON:0001759)
            paramType: query
          - name: target_predicate
            description: The predicate for the type of connectivity (eg ilxtr:hasPresynapticTerminalsIn)
            paramType: query
            required: false

  - path: /dynamic/demos/apinat/bundles/{start-id}
    operations:
      - summary: Return the paths to somas from an anatomical region (aka connected-somas)
        parameters:
         - name: start-id
           description: ontology id of the starting point
           paramType: path
    query: |
      MATCH path1 = (start:Class{iri: "${start-id}"})
      -[:apinatomy:annotates]->(start_housing)
      -[:apinatomy:subtypes*0..1]->()
      -[:apinatomy:clones*0..1]->(layer_or_end)
      -[:apinatomy:layers*0..1]->()
      -[:apinatomy:bundles]->(linkStart)
      -[:apinatomy:prevChainEndLevels|apinatomy:prev|apinatomy:source*1..]->(link)
      -[:apinatomy:targetOf|apinatomy:sourceOf]->(linkSoma)  // axon or dendrite root
      -[:apinatomy:conveyingLyph]->()
      -[:apinatomy:supertype*0..1]->(soma:NamedIndividual)
      -[:apinatomy:ontologyTerms]->(c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
      WITH path1, link
      OPTIONAL MATCH path2 = (link)
      -[:apinatomy:fasciculatesIn|apinatomy:endsIn]->(layer_or_end)
      -[:apinatomy:layerIn*0..1]->(end)
      -[:apinatomy:ontologyTerms]->(external)
      RETURN path1, path2

  - path: /dynamic/demos/apinat/old-bundles/{start-id}
    operations:
      - summary: Return the paths to somas from an anatomical region (aka connected-somas)
        parameters:
         - name: start-id
           description: ontology id of the starting point
           paramType: path
    query: |
      MATCH path1 = (start:Class{iri: '${start-id}'})
      -[:apinatomy:annotates]->(start_housing)
      -[:apinatomy:bundlesChains]->(chain)
      -[:apinatomy:root]->(root)
      -[:apinatomy:internalIn]->(layer_or_end)  # this hits a cycle back to start_housing
      -[:apinatomy:cloneOf*0..1]->()
      -[:apinatomy:supertype*0..1]->()
      -[:apinatomy:ontologyTerms]->(layer_or_end_external)
      WITH path1, root, layer_or_end AS layer
      OPTIONAL MATCH path2 = (layer)
      -[:apinatomy:layerIn]->(end_housing)
      -[:apinatomy:ontologyTerms]->(end_housing_external)
      WITH path1, path2, root
      MATCH path3 = (root) // in the layer case this hits an additional lyph
      <-[:apinatomy:target|apinatomy:source]-(link)
      <-[:apinatomy:conveys]-(soma)
      <-[:apinatomy:annotates]-(soma_NLX)
      RETURN path1, path2, path3

  - path: /dynamic/demos/apinat/somas
    operations:
      - summary: List all the somas for a given graph (TODO on the given graph)
    query: |
      MATCH (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
      -[:apinatomy:annotates]->(soma:NamedIndividual)
      RETURN soma

  - path: /dynamic/demos/apinat/housing-lyphs
    operations:
      - summary: List all the housing lyphs (neuronal processes) for all starting points.
    query: |
      MATCH path = (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
      -[:apinatomy:annotates]->(soma:NamedIndividual)  // soma lyph
      -[:apinatomy:conveys]->(somaLink)                // link connecting soma to axon and dendrite
      -[:apinatomy:target|apinatomy:source]->(root)    // axon or dendrite root
      -[:apinatomy:controlNodes|apinatomy:rootOf*1..2]->(chain)                    // axon or dendrite tree
      -[:apinatomy:housingLyphs]->(housing)            // list of lyphs housing the trees
      -[:apinatomy:ontologyTerms*0..1]->(external)          // external ids for the housing lyphs
      WHERE soma.`https://apinatomy.org/uris/readable/generated` IS NULL
      RETURN path

  - path: /dynamic/demos/apinat/housing-lyphs/{start-id}
    operations:
      - summary: List all the housing lyphs for a starting point.
        parameters:
         - name: start-id
           description: ontology id of the starting point
           paramType: path
    query: |
      MATCH path1 = (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
      -[:apinatomy:annotates]->(soma:NamedIndividual)  // soma lyph
      -[:apinatomy:conveys]->(somaLink)                // link connecting soma to axon and dendrite
      -[:apinatomy:target|apinatomy:source]->(root)    // axon or dendrite root
      -[:apinatomy:internalIn]->(layer_or_end)
      -[:apinatomy:cloneOf*0..1]->()
      -[:apinatomy:supertype*0..1]->()
      -[:apinatomy:ontologyTerms]->(layer_or_end_external:Class{iri: '${start-id}'})
      WHERE soma.`https://apinatomy.org/uris/readable/generated` IS NULL
      WITH path1, root
      MATCH path2 = (root)
      -[:apinatomy:controlNodes|apinatomy:rootOf*1..2]->(chain)                    // axon or dendrite tree
      -[:apinatomy:housingLyphs]->(housing)            // list of lyphs housing the trees
      -[:apinatomy:ontologyTerms*0..1]->(external)          // external ids for the housing lyphs
      RETURN path1, path2

      UNION

      MATCH path1 = (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
      -[:apinatomy:annotates]->(soma:NamedIndividual)  // soma lyph
      -[:apinatomy:conveys]->(somaLink)                // link connecting soma to axon and dendrite
      -[:apinatomy:target|apinatomy:source]->(root)    // axon or dendrite root
      -[:apinatomy:internalIn]->(layer)
      -[:apinatomy:cloneOf*0..1]->()
      -[:apinatomy:supertype*0..1]->()
      -[:apinatomy:layerIn]->(end_housing)
      -[:apinatomy:ontologyTerms]->(end_housing_external:Class{iri: '${start-id}'})
      WHERE soma.`https://apinatomy.org/uris/readable/generated` IS NULL
      WITH path1, root
      MATCH path2 = (root)
      -[:apinatomy:rootOf]->(chain)                    // axon or dendrite tree
      -[:apinatomy:housingLyphs]->(housing)            // list of lyphs housing the trees
      -[:apinatomy:ontologyTerms*0..1]->(external)          // external ids for the housing lyphs
      RETURN path1, path2

  - path: /dynamic/demos/apinat/soma-processes
    operations:
      - summary: List all the neuronal processes for all somas.
    query: |
      MATCH path1 = (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
      -[:apinatomy:annotates]->(soma:NamedIndividual)    // soma lyph
      -[:apinatomy:conveys]->(linkSoma)                  // link connecting soma to axon and dendrite
      -[:apinatomy:target|apinatomy:source]->(nodeRoot)  // axon or dendrite root
      -[:apinatomy:sourceOf|apinatomy:nextChainStartLevels|apinatomy:next*1..]->(link)  // sourceOf is first and only once
      -[:apinatomy:fasciculatesIn|apinatomy:endsIn]->(layer_or_end)
      -[:apinatomy:cloneOf*0..1]->()
      -[:apinatomy:supertype*0..1]->()
      -[:apinatomy:ontologyTerms]->(external)
      WHERE soma.`https://apinatomy.org/uris/readable/generated` IS NULL
      WITH path1, nodeRoot, layer_or_end AS layer
      OPTIONAL MATCH path2 = (layer)  // if we were in a layer, get the containing lyph as well
      -[:apinatomy:layerIn]->(end_housing)
      -[:apinatomy:ontologyTerms]->(end_housing_external)
      WITH path1, path2, nodeRoot
      MATCH path3 = (nodeRoot)        // extract chain for axon vs dendrite
      -[:apinatomy:rootOf]->(chain)
      RETURN path1, path2, path3

  - path: /dynamic/demos/apinat/soma-processes/{start-id}
    operations:
      - summary: List all the neuronal processes for somas located in start-id.
        parameters:
         - name: start-id
           description: ontology id of the starting point
           paramType: path
    query: |
      MATCH path1 = (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
      -[:apinatomy:annotates]->(soma:NamedIndividual)    // soma lyph
      -[:apinatomy:conveys]->(linkSoma)                  // link connecting soma to axon and dendrite
      -[:apinatomy:target|apinatomy:source]->(nodeRoot)  // axon or dendrite root
      -[:apinatomy:internalIn]->(layer_or_end)
      -[:apinatomy:cloneOf*0..1]->()
      -[:apinatomy:supertype*0..1]->()
      -[:apinatomy:layerIn*0..1]->(layerSoma)  // don't need to see both layer and housing for soma
      -[:apinatomy:ontologyTerms]->(externalEndSoma:Class{iri: '${start-id}'})
      WHERE soma.`https://apinatomy.org/uris/readable/generated` IS NULL
      WITH path1, nodeRoot
      MATCH path3 = (chain)
      <-[:apinatomy:rootOf]-(nodeRoot)
      -[:apinatomy:sourceOf|apinatomy:nextChainStartLevels|apinatomy:next*1..]->(link)
      -[:apinatomy:fasciculatesIn|apinatomy:endsIn]->(layer_or_end)
      -[:apinatomy:cloneOf*0..1]->()
      -[:apinatomy:supertype*0..1]->()
      -[:apinatomy:ontologyTerms]->(external)
      WITH path1, path3, nodeRoot, layer_or_end AS layer
      OPTIONAL MATCH path2 = (layer)  // if we were in a layer, get the containing lyph as well
      -[:apinatomy:layerIn]->(end_housing)
      -[:apinatomy:ontologyTerms]->(end_housing_external)
      RETURN path1, path2, path3

  - path: /dynamic/demos/apinat/weird-soma-processes/{process-id}
    operations:
      - summary: List all the neuronal processes for somas where some processes is in process-id.
        parameters:
         - name: process-id
           description: ontology id of the starting point
           paramType: path
    query: |
      MATCH path1 = (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
      -[:apinatomy:annotates]->(soma:NamedIndividual)    // soma lyph
      -[:apinatomy:conveys]->(linkSoma)                  // link connecting soma to axon and dendrite
      -[:apinatomy:target|apinatomy:source]->(nodeRoot)  // axon or dendrite root
      -[:apinatomy:sourceOf|apinatomy:nextChainStartLevels|apinatomy:next*1..]->(link)  // sourceOf is first and only once
      -[:apinatomy:fasciculatesIn|apinatomy:endsIn]->(layer_or_end)
      -[:apinatomy:cloneOf*0..1]->()
      -[:apinatomy:supertype*0..1]->()
      -[:apinatomy:ontologyTerms]->(external:Class{iri: '${process-id}'})
      WHERE soma.`https://apinatomy.org/uris/readable/generated` IS NULL
      WITH path1, nodeRoot, layer_or_end AS layer
      OPTIONAL MATCH path2 = (layer)  // if we were in a layer, get the containing lyph as well
      -[:apinatomy:layerIn]->(end_housing)
      -[:apinatomy:ontologyTerms]->(end_housing_external)
      WITH path1, path2, nodeRoot
      MATCH path3 = (nodeRoot)        // extract chain for axon vs dendrite
      -[:apinatomy:rootOf]->(chain)
      RETURN path1, path2, path3

      UNION

      MATCH path1 = (c:Class{iri: "http://uri.neuinfo.org/nif/nifstd/nlx_154731"})
      -[:apinatomy:annotates]->(soma:NamedIndividual)    // soma lyph
      -[:apinatomy:conveys]->(linkSoma)                  // link connecting soma to axon and dendrite
      -[:apinatomy:target|apinatomy:source]->(nodeRoot)  // axon or dendrite root
      -[:apinatomy:sourceOf|apinatomy:nextChainStartLevels|apinatomy:next*1..]->(link)  // sourceOf is first and only once
      -[:apinatomy:fasciculatesIn|apinatomy:endsIn]->(layer_or_end)
      -[:apinatomy:cloneOf*0..1]->()
      -[:apinatomy:supertype*0..1]->()
      -[:apinatomy:ontologyTerms]->(external)
      WHERE soma.`https://apinatomy.org/uris/readable/generated` IS NULL
      WITH path1, nodeRoot, layer_or_end AS layer
      MATCH path2 = (layer)  // if we were in a layer, get the containing lyph as well
      -[:apinatomy:layerIn]->(end_housing)
      -[:apinatomy:ontologyTerms]->(end_housing_external:Class{iri: '${process-id}'})
      WITH path1, path2, nodeRoot
      MATCH path3 = (nodeRoot)        // extract chain for axon vs dendrite
      -[:apinatomy:rootOf]->(chain)
      RETURN path1, path2, path3

  - path: /dynamic/demos/apinat/neru-1/{neupop-id}
    operations: &neru-ops
      - summary: Return the housing regions and publications for neurulated groups.
        parameters:
         - name: neupop-id
           description: neuron population identifier
           paramType: path
    query: |
      MATCH (neupop:Class{iri: $neupop_id})
      -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK

      // publications
      WITH neugrp, a
      OPTIONAL MATCH path = (neugrp)
      -[:apinatomy:references]->(pub)
      -[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Publication"}) // cannot be curied, dynamic endpoints will not expand it

      WITH neugrp, a, path
      MATCH (neugrp)
      -[b:apinatomy:links]->(link)
      -[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
      -[d:apinatomy:layerIn*0..1]->(lyph)
      -[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph

      WITH lyph, a, b, c, d, path
      MATCH (lyph)
      -[e:apinatomy:ontologyTerms]->(region)
      RETURN a, b, c, d, e, path

      UNION

      // this part usually only returns the soma housing lyph
      MATCH (neupop:Class{iri: $neupop_id})
      -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
      -[b:apinatomy:lyphs]->(lyph)
      -[c:apinatomy:internalIn]->(e) // e is a hack to get columns to match
      -[d:apinatomy:ontologyTerms*0..1]->(region)
      // this variant shows the dead end lyphs that correspond to the fasciculatesIn links above
      //-[c:apinatomy:internalIn*0..1]->(e)
      //-[d:apinatomy:ontologyTerms*0..1]->(region)
      return a, b, c, d, null AS e, null AS path

  - path: /dynamic/demos/apinat/neru-2/{neupop_id}
    operations: *neru-ops
    query: |
      MATCH (neupop:Class{iri: $neupop_id})
      -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK

      // publications
      WITH neugrp, a
      OPTIONAL MATCH path = (neugrp)
      -[:apinatomy:references]->(pub)
      -[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Publication"}) // cannot be curied, dynamic endpoints will not expand it

      WITH neugrp, a, path
      MATCH (neugrp)
      -[b:apinatomy:links]->(link)
      -[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
      -[d:apinatomy:layerIn*0..1]->(lyph)
      -[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph

      WITH neugrp, link, lyph, a, b, c, d, path
      MATCH (lyph)
      -[e:apinatomy:ontologyTerms]->(region)

      // use apinatomy:next to extract ordering information
      WITH neugrp, link, a, b, c, d, e, path
      MATCH (link)
      -[f:apinatomy:next*0..]->()
      //-[f:apinatomy:next|apinatomy:nextChainStartLevels*0..]->()
      // FIXME these should be collapsing into a single relationship
      -[g:apinatomy:target*0..1]->()
      -[h:apinatomy:rootOf*0..1]->()
      -[i:apinatomy:levels*0..1]->()
      <-[:apinatomy:links]-(neugrp)

      RETURN a, b, c, d, e, f, g,h,i, path

      UNION

      // this part usually only returns the soma housing lyph
      MATCH (neupop:Class{iri: $neupop_id})
      -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
      -[b:apinatomy:lyphs]->(lyph)
      -[c:apinatomy:internalIn]->(e) // e is a hack to get columns to match
      -[d:apinatomy:ontologyTerms*0..1]->(region)
      // this variant shows the dead end lyphs that correspond to the fasciculatesIn links above
      //-[c:apinatomy:internalIn*0..1]->(e)
      //-[d:apinatomy:ontologyTerms*0..1]->(region)
      RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path

  - path: /dynamic/demos/apinat/neru-3/{neupop_id}
    operations: *neru-ops
    query: |
      MATCH (neupop:Class{iri: $neupop_id})
      -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK

      // publications
      WITH neugrp, a
      OPTIONAL MATCH path = (neugrp)
      -[:apinatomy:references]->(pub)
      -[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Publication"}) // cannot be curied, dynamic endpoints will not expand it

      WITH neugrp, a, path
      MATCH (neugrp)
      -[b:apinatomy:links]->(link)
      -[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
      -[d:apinatomy:layerIn*0..1]->(lyph)
      -[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph

      WITH neugrp, link, lyph, a, b, c, d, path // MATCH vs , not all things that match as lyphs have externals
      MATCH (lyph)
      -[e:apinatomy:ontologyTerms]->(region)

      // use apinatomy:next to extract ordering information
      WITH neugrp, link, a, b, c, d, e, path
      MATCH p2 = (link)
      -[:apinatomy:conveyingLyph]->(cl)
      -[:apinatomy:topology]->()

      WITH neugrp, link, a, b, c, d, e, path, p2, cl
      MATCH (cl)
      -[x:apinatomy:inheritedOntologyTerms*0..1]->()

      WITH neugrp, link, a, b, c, d, e, path, p2, x
      MATCH (link)
      -[f:apinatomy:next*0..]->()
      //-[f:apinatomy:next|apinatomy:nextChainStartLevels*0..]->()
      // FIXME these should be collapsing into a single relationship
      -[g:apinatomy:target*0..1]->()
      -[h:apinatomy:rootOf*0..1]->()
      -[i:apinatomy:levels*0..1]->()
      <-[:apinatomy:links]-(neugrp)

      RETURN a, b, c, d, e, f, g,h,i, path, p2, x

      UNION

      // this part usually only returns the soma housing lyph
      MATCH (neupop:Class{iri: $neupop_id})
      -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
      -[b:apinatomy:lyphs]->(lyph)
      -[c:apinatomy:internalIn]->()
      -[d:apinatomy:ontologyTerms*0..1]->(region)
      // this variant shows the dead end lyphs that correspond to the fasciculatesIn links above
      //-[c:apinatomy:internalIn*0..1]->()
      //-[d:apinatomy:ontologyTerms*0..1]->(region)
      RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path, null as p2, null as x

  - path: /dynamic/demos/apinat/neru-4/{neupop_id}
    operations: *neru-ops
    query: |
      MATCH (neupop:Class{iri: $neupop_id})
      -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK

      , (neugrp)
      -[:apinatomy:links]->(link)
      -[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
      -[d:apinatomy:layerIn*0..1]->(lyph)
      -[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph

      WITH neugrp, link, lyph, a, c, d
      OPTIONAL MATCH layer_ext = (lyph)
      <-[d*1]-(layer)-[:apinatomy:cloneOf]->()-[:apinatomy:inheritedOntologyTerms]->()

      WITH neugrp, link, lyph, a, c, d, layer_ext // there is a difference here because the previous match does not require lyphs to have external ids
      MATCH (lyph)
      -[e:apinatomy:ontologyTerms]->(region)

      , p2 = (link)
      -[:apinatomy:conveyingLyph]->(cl)
      -[:apinatomy:topology]->()

      , (cl)
      -[x:apinatomy:inheritedOntologyTerms*0..1]->()

      // use apinatomy:next to extract ordering information
      , (link)
      -[f:apinatomy:next*0..]->()
      -[g:apinatomy:target*0..1]->()
      -[h:apinatomy:rootOf*0..1]->()
      -[i:apinatomy:levels*0..1]->()
      <-[:apinatomy:links]-(neugrp)

      // publications
      WITH neugrp, a, c, d, e, f, g,h,i, p2, x, layer_ext
      OPTIONAL MATCH path = (neugrp)
      -[:apinatomy:references]->(pub)
      -[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Publication"}) // cannot be curied, dynamic endpoints will not expand it

      RETURN a, null as b, c, d, e, f, g,h,i, path, p2, x, layer_ext

      UNION

      // this part usually only returns the soma housing lyph
      MATCH (neupop:Class{iri: $neupop_id})
      -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
      -[b:apinatomy:lyphs]->(lyph)
      -[c:apinatomy:internalIn]->()
      -[d:apinatomy:ontologyTerms*0..1]->(region)
      , p2 = (lyph)
      -[:apinatomy:conveys]->(soma_link)
      -[:apinatomy:source|apinatomy:target]->(soma_node)
      -[:apinatomy:sourceOf]->(chain_link)
      , (chain_link)
      -[:apinatomy:levelIn]->(chain)
      , (soma_node)
      -[:apinatomy:rootOf]->(chain)

      RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path, p2, null as x, null as layer_ext

  - path: /dynamic/demos/apinat/neru-5/{neupop_id}
    operations: *neru-ops
    query: |
      MATCH (neupop:Class{iri: $neupop_id})
      -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK

      , (neugrp)
      -[:apinatomy:links]->(link)
      -[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
      -[d:apinatomy:layerIn*0..1]->(lyph)
      -[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph

      WITH neugrp, link, lyph, a, c, d
      OPTIONAL MATCH layer_ext = (lyph)
      <-[d*1]-(layer)-[:apinatomy:cloneOf*0..1]->()-[:apinatomy:inheritedExternal]->()

      WITH neugrp, link, lyph, a, c, d, layer_ext
      OPTIONAL MATCH more = (lyph)
      -[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
      -[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()

      WITH neugrp, link, lyph, a, c, d, layer_ext, more // there is a difference here because the previous match does not require lyphs to have external ids
      MATCH (lyph)
      -[e:apinatomy:ontologyTerms]->(region)

      , p2 = (link)
      -[:apinatomy:conveyingLyph]->(cl)
      -[:apinatomy:topology]->()

      , (cl)
      -[x:apinatomy:inheritedExternal*0..1]->()

      // use apinatomy:next to extract ordering information
      , (link)
      -[f:apinatomy:next*0..]->()
      -[g:apinatomy:target*0..1]->()
      -[h:apinatomy:rootOf*0..1]->()
      -[i:apinatomy:levels*0..1]->()
      <-[:apinatomy:links]-(neugrp)

      // publications
      WITH neugrp, a, c, d, e, f, g,h,i, p2, x, layer_ext, more
      OPTIONAL MATCH path = (neugrp)
      -[:apinatomy:references]->(pub)
      -[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Reference"}) // cannot be curied, dynamic endpoints will not expand it

      RETURN a, null as b, c, d, e, f, g,h,i, path, p2, x, layer_ext, more

      UNION

      // this part usually only returns the soma housing lyph
      MATCH (neupop:Class{iri: $neupop_id})
      -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
      -[b:apinatomy:lyphs]->(lyph)
      -[c:apinatomy:internalIn]->()
      -[d:apinatomy:ontologyTerms*0..1]->(region)
      , p2 = (lyph)
      -[:apinatomy:conveys]->(soma_link)
      -[:apinatomy:source|apinatomy:target]->(soma_node)
      -[:apinatomy:sourceOf]->(chain_link)
      , (chain_link)
      -[:apinatomy:levelIn]->(chain)
      , (soma_node)
      -[:apinatomy:rootOf]->(chain)

      WITH lyph, a, b, c, d, p2
      OPTIONAL MATCH more = (lyph)
      -[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
      -[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()

      RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path, p2, null as x, null as layer_ext, more

  - path: /dynamic/demos/apinat/neru-6/{neupop_id}
    operations: *neru-ops
    query: |
      MATCH (neupop:Class{iri: $neupop_id})
      -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK

      , (neugrp)
      -[:apinatomy:links]->(link)
      -[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
      -[d:apinatomy:layerIn*0..1]->(lyph)
      -[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph

      WITH neugrp, link, lyph, a, c, d
      OPTIONAL MATCH layer_ext = (lyph)
      <-[d*1]-(layer)-[:apinatomy:cloneOf*0..1]->()-[:apinatomy:inheritedExternal]->()

      WITH neugrp, link, lyph, a, c, d, layer_ext
      OPTIONAL MATCH more = (lyph)
      -[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
      -[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()

      WITH neugrp, link, lyph, a, c, d, layer_ext, more // there is a difference here because the previous match does not require lyphs to have external ids
      MATCH (lyph)
      -[e:apinatomy:ontologyTerms]->(region)

      , p2 = (link)
      -[:apinatomy:conveyingLyph]->(cl)
      -[:apinatomy:topology]->()

      , (cl)
      -[x:apinatomy:inheritedExternal*0..1]->()

      // use apinatomy:next to extract ordering information
      , (link)
      -[f:apinatomy:next|apinatomy:nextChainStartLevels*0..]->()
      -[g:apinatomy:target*0..1]->()
      -[h:apinatomy:rootOf*0..1]->()
      -[i:apinatomy:levels*0..1]->()
      <-[:apinatomy:links]-(neugrp)

      // publications
      WITH neugrp, a, c, d, e, f, g,h,i, p2, x, layer_ext, more
      OPTIONAL MATCH path = (neugrp)
      -[:apinatomy:references]->(pub)
      -[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Reference"}) // cannot be curied, dynamic endpoints will not expand it

      RETURN a, null as b, c, d, e, f, g,h,i, path, p2, x, layer_ext, more

      UNION

      // this part usually only returns the soma housing lyph
      MATCH (neupop:Class{iri: $neupop_id})
      -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
      -[b:apinatomy:lyphs]->(lyph)
      -[c:apinatomy:internalIn]->()
      -[d:apinatomy:ontologyTerms*0..1]->(region)
      , p2 = (lyph)
      -[:apinatomy:conveys]->(soma_link)
      -[:apinatomy:source|apinatomy:target]->(soma_node)
      -[:apinatomy:sourceOf]->(chain_link)
      , (chain_link)
      -[:apinatomy:levelIn]->(chain)
      , (soma_node)
      -[:apinatomy:rootOf]->(chain)

      WITH lyph, a, b, c, d, p2
      OPTIONAL MATCH more = (lyph)
      -[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
      -[:apinatomy:ontologyTerms|apinatomy:inheritedExternal]->()

      RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path, p2, null as x, null as layer_ext, more

  - path: /dynamic/demos/apinat/neru-7/{neupop_id}
    operations: *neru-ops
    query: |
      MATCH (neupop:Class{iri: $neupop_id})
      -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK

      , (neugrp)
      -[:apinatomy:links]->(link)
      -[c:apinatomy:fasciculatesIn|apinatomy:endsIn*0..1]->(lyph_or_layer) // real lyphs convey things, layers do not
      -[d:apinatomy:layerIn*0..1]->(lyph)
      -[:apinatomy:conveys*0..1]->() // make sure we are at a real lyph

      WITH neugrp, link, lyph, a, c, d
      OPTIONAL MATCH layer_ext = (lyph)
      <-[d*1]-(layer)-[:apinatomy:cloneOf*0..1]->()-[:apinatomy:inheritedOntologyTerms]->()

      WITH neugrp, link, lyph, a, c, d, layer_ext
      OPTIONAL MATCH more = (lyph)
      -[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
      -[:apinatomy:ontologyTerms|apinatomy:inheritedOntologyTerms]->()

      WITH neugrp, link, lyph, a, c, d, layer_ext, more // there is a difference here because the previous match does not require lyphs to have external ids
      MATCH (lyph)
      -[e:apinatomy:ontologyTerms]->(region)

      , p2 = (link)
      -[:apinatomy:conveyingLyph]->(cl)
      -[:apinatomy:topology]->()

      , (cl)
      -[x:apinatomy:inheritedOntologyTerms*0..1]->()

      // use apinatomy:next to extract ordering information
      , (link)
      -[f:apinatomy:next|apinatomy:nextChainStartLevels*0..]->()
      -[g:apinatomy:target*0..1]->()
      -[h:apinatomy:rootOf*0..1]->()
      -[i:apinatomy:levels*0..1]->()
      <-[:apinatomy:links]-(neugrp)

      // publications
      WITH neugrp, a, c, d, e, f, g,h,i, p2, x, layer_ext, more
      OPTIONAL MATCH path = (neugrp)
      -[:apinatomy:references]->(pub)
      -[:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Reference"}) // cannot be curied, dynamic endpoints will not expand it

      RETURN a, null as b, c, d, e, f, g,h,i, path, p2, x, layer_ext, more

      UNION

      // this part usually only returns the soma housing lyph
      MATCH (neupop:Class{iri: $neupop_id})
      -[a:apinatomy:annotates]->(neugrp:NamedIndividual{`https://apinatomy.org/uris/readable/description`: "dynamic"}) // FIXME HACK
      -[b:apinatomy:lyphs]->(lyph)
      -[c:apinatomy:internalIn]->()
      -[d:apinatomy:ontologyTerms*0..1]->(region)
      , p2 = (lyph)
      -[:apinatomy:conveys]->(soma_link)
      -[:apinatomy:source|apinatomy:target]->(soma_node)
      -[:apinatomy:sourceOf]->(chain_link)
      , (chain_link)
      -[:apinatomy:levelIn]->(chain)
      , (soma_node)
      -[:apinatomy:rootOf]->(chain)

      WITH lyph, a, b, c, d, p2
      OPTIONAL MATCH more = (lyph)
      -[:apinatomy:layerIn|apinatomy:endsIn|apinatomy:fasciculatesIn|apinatomy:internalIn|apinatomy:cloneOf*1..]->()
      -[:apinatomy:ontologyTerms|apinatomy:inheritedOntologyTerms]->()

      RETURN a, b, c, d, null AS e, null AS f, null AS g, null AS h, null AS i, null AS path, p2, null as x, null as layer_ext, more

  - path: /dynamic/demos/apinat/modelList
    operations:
      - summary: nil
    query: |
      MATCH ({iri: "https://apinatomy.org/uris/elements/Graph"})
      <-[:type]-(g)-[:isDefinedBy]->(o:Ontology)
      RETURN o

  - path: /dynamic/demos/apinat/modelPopulationsReferences/{model_id}
    operations:
      - summary: nil
        parameters:
         - name: model_id
           description: the identifier for an ApiNATOMY model
           paramType: path
    query: |
      MATCH (start:Ontology {iri: $model_id})
      <-[:isDefinedBy]-(external:Class)
      -[:subClassOf*]->(:Class {iri: "http://uri.interlex.org/tgbugs/uris/readable/NeuronEBM"}) // FIXME
      ,
      (external)
      -[e:type]->()
      RETURN e
      UNION
      OPTIONAL MATCH (start:Ontology {iri: $model_id})
      <-[:isDefinedBy]-(graph:NamedIndividual)
      -[:type]->({iri: "https://apinatomy.org/uris/elements/Graph"}) // elements don't have a superclass right now
      ,
      (graph)
      -[:apinatomy:references]->(pub)
      -[e:type]->(:Class{iri: "https://apinatomy.org/uris/elements/Reference"})
      RETURN e

ApiNATOMY competency queries

expected counts per model

Expected number of somas and neupops.

model-id neupops somas
ard-arm-cardiac 12 41
bolser-lewis 29 29
bronchomotor 6 19
keast-bladder 20 34
sawg-distal-colon 17 23
sawg-stomach 14 14

somas

somas >= neupops

The number of somas for distinct populations in a given model.

populations

the number of neuron populations as defined by the neurulator that have an ontology identifier

neuron part per region per model

model-id diag abbrev external somas axons axon term dend dend term
bolser-lewis SCG   17        
keast-bladder              
bronchomotor              
ard-arm-cardiac              
sawg-distal-colon              
sawg-stomach              

expected locations

use NPO as a top down constraint on the locations of somas etc.

somas

could do this by population or generically

population process location

soma-processes can also be checked using NPO but not needed for simple cases

model-id neupop diag abbrev external
bolser-lewis q PN  
bolser-lewis q C4  
bolser-lewis q PHRNIC  
bolser-lewis q DIAPHRAGM  

location process populations

bundles NPO here as well

model-id diag abbrev external neupop
bolser-lewis VAGUS   9 total

cross model

expect populations from models to have processes in common location

select distinct
?external
(str(?el) as ?label)
#?p
?model
#?region
#?link
#?neru
# ?seed
(replace(str(?neru), "^.+-([A-Za-z0-9]+)$", "$1") as ?neuron_number)
#?dyn
?neupop
where {
  values ?external { UBERON:0006457 UBERON:0001894 UBERON:0005453 UBERON:0018683 }

  ?external a owl:Class ;
    apinatomy:annotates ?region ;
    rdfs:label ?el .

  ?region a elements:Lyph .

  ?model a elements:Graph ;
    apinatomy:lyphs ?region .

  ?link ( apinatomy:fasciculatesIn | apinatomy:endsIn | apinatomy:layerIn | apinatomy:internalIn )+ ?region .

  ?neru apinatomy:links ?link ;
    apinatomy:description ?dyn ;
    a elements:Group .
  filter contains(str(?dyn), "dynamic")
  optional { ?neru apinatomy:ontologyTerms ?neupop . }
  optional { ?seed apinatomy:seedIn ?neru . } # seedIn sort of works but misses cases where a dynamic group was created with a seed
}
order by str(?external) str(?model) xsd:integer(?neuron_number)

Expected.

external diag abbrev model-id neupop
UBERON:0006457 T1 bolser-lewis 1 2 3 6 7
UBERON:0006457 T1 keast-bladder 20
UBERON:0001894 DIENC bolser-lewis 1 26
UBERON:0001894 Diencephalon keast-bladder 13 14 15 16 17 18
UBERON:0005453 IMG keast-bladder 3 6 7 11
UBERON:0005453 IMG sawg-distal-colon B D F G H Q
UBERON:0018683 LumSplN keast-bladder 6 7 11
UBERON:0018683 LumSplN sawg-distal-colon B

vagus

OWL DL query

hasLocationPhenotype some ('part of' some 'vagus nerve')
hasLocationPhenotype some ('part of' some 'vagus nerve nucleus')
hasLocationPhenotype some ('part of' some 'vagus nerve' or 'part of' some 'vagus nerve nucleus')
// filter out things that project to the vagus nerve not that start in the vagus nerve or something like that
(neupop)-[:ilxtr:hasLocationPhenotype]->(:Class{iri: UBERON:0001759})
(neupop)-[:ilxtr:hasAxonPresynapticElementIn]->(axon_locations)
(neupop)-[:ilxtr:hasSensorySubcellularElementIn]->(dendrite_locations)

visualize all populations that have some process in the IMG

Images of nerus that have some process in the IMG, from keast-bladder and sawg-distal-colon.

kg queries

TODO point people to the query that returns a list of all neuron populations and all populations + papers per model 5 is going to be the easiest

neuron type keast 5 projects to

RETURN region

Expected.

external label
UBERON:0016508 pelvic ganglion

img processes query

Input

UBERON:0005453 inferior mesenteric ganglion

Expect

ilxtr:neuron-type-keast-3
ilxtr:neuron-type-keast-6
ilxtr:neuron-type-keast-7
ilxtr:neuron-type-keast-11
ilxtr:neuron-type-sdcol-b
ilxtr:neuron-type-sdcol-d
ilxtr:neuron-type-sdcol-f
ilxtr:neuron-type-sdcol-g
ilxtr:neuron-type-sdcol-h
ilxtr:neuron-type-sdcol-q

Simple blaze

simple sckan queries over blazegraph

inserts

insert { ?s ?np ?o }
where
{
  {
    values (?p ?np) {
      (ilxtr:hasAxonPresynapticElementIn        nposimtest:hasAxonPresynapticElementIn)
      (ilxtr:hasSomaLocatedIn                   nposimtest:hasSomaLocatedIn)
      #(ilxtr:hasSomaLocatedIn                   nposimtest:hasAxonLocatedIn) # add inferred axon locations (decided to exclude)
      (ilxtr:hasAxonLocatedIn                   nposimtest:hasAxonLocatedIn)
      (ilxtr:hasDendriteLocatedIn               nposimtest:hasDendriteLocatedIn)
      (ilxtr:hasAxonSensorySubcellularElementIn nposimtest:hasAxonSensorySubcellularElementIn)

      #(ilxtr:hasAxonPresynapticElementIn ilxtr:hasAxonTerminalLocation)
      #(ilxtr:hasSomaLocatedIn ilxtr:hasSomaLocation)
      #(ilxtr:hasAxonLocatedIn ilxtr:hasAxonLocation)
      #(ilxtr:hasDendriteLocatedIn ilxtr:hasDendriteLocation)
      #(ilxtr:hasAxonSensorySubcellularElementIn ilxtr:hasAxonSensoryLocation)
    }
    ?s owl:equivalentClass [
                             rdf:type owl:Class ;
                             owl:intersectionOf ?bn0
                           ] .
    ?bn0 rdf:rest*/rdf:first ilxtr:NeuronApinatSimple .
    ?bn0 rdf:rest*/rdf:first [
                               rdf:type owl:Restriction ;
                               owl:onProperty ?p ;
                               owl:someValuesFrom [
                                                    a owl:Restriction ;
                                                    owl:onProperty partOf: ;
                                                    owl:someValuesFrom ?o
                                                  ] ]

  }
  union
  {
    values (?p ?np) {
      (ilxtr:hasForwardConnectionPhenotype     nposimtest:hasForwardConnectionPhenotype)
      (ilxtr:hasFunctionalCircuitRolePhenotype nposimtest:hasFunctionalCircuitRolePhenotype)
    }
    ?s owl:equivalentClass [
                             rdf:type owl:Class ;
                             owl:intersectionOf ?bn0
                           ] .
    ?bn0 rdf:rest*/rdf:first ilxtr:NeuronApinatSimple .
    ?bn0 rdf:rest*/rdf:first [
                               rdf:type owl:Restriction ;
                               owl:onProperty ?p ;
                               owl:someValuesFrom ?o ]
  }}

queries

#select (count(*) as ?c) {
select distinct ?s ?o
where
{
  #?s nposimtest:hasSomaLocatedIn ?o
  ?s nposimtest:hasDendriteLocatedIn ?o
} order by ?s ?o
#}
PREFIX nposimtest: <http://uri.interlex.org/tgbugs/uris/readable/npo-simple-test-1/>
SELECT (COUNT(*) as ?count) {
  #PREFIX nposimtest: <http://uri.interlex.org/tgbugs/uris/readable/npo-simple-test-1/>
  select
  distinct ?s ?np ?o
  where
  {
    {
      values (?p ?np) {
        (ilxtr:hasAxonPresynapticElementIn        nposimtest:hasAxonPresynapticElementIn)
        (ilxtr:hasSomaLocatedIn                   nposimtest:hasSomaLocatedIn)
        #(ilxtr:hasSomaLocatedIn                   nposimtest:hasAxonLocatedIn) # add inferred axon locations (decided to exclude)
        (ilxtr:hasAxonLocatedIn                   nposimtest:hasAxonLocatedIn)
        (ilxtr:hasDendriteLocatedIn               nposimtest:hasDendriteLocatedIn)
        (ilxtr:hasAxonSensorySubcellularElementIn nposimtest:hasAxonSensorySubcellularElementIn)

        #(ilxtr:hasAxonPresynapticElementIn ilxtr:hasAxonTerminalLocation)
        #(ilxtr:hasSomaLocatedIn ilxtr:hasSomaLocation)
        #(ilxtr:hasAxonLocatedIn ilxtr:hasAxonLocation)
        #(ilxtr:hasDendriteLocatedIn ilxtr:hasDendriteLocation)
        #(ilxtr:hasAxonSensorySubcellularElementIn ilxtr:hasAxonSensoryLocation)
      }
      ?s owl:equivalentClass [
                               rdf:type owl:Class ;
                               owl:intersectionOf ?bn0
                             ] .
      ?bn0 rdf:rest*/rdf:first ilxtr:NeuronApinatSimple .
      ?bn0 rdf:rest*/rdf:first [
                                 rdf:type owl:Restriction ;
                                 owl:onProperty ?p ;
                                 owl:someValuesFrom [
                                                      a owl:Restriction ;
                                                      owl:onProperty partOf: ;
                                                      owl:someValuesFrom ?o
                                                    ] ]

    }
    union
    {
      values (?p ?np) {
        (ilxtr:hasForwardConnectionPhenotype     nposimtest:hasForwardConnectionPhenotype)
        (ilxtr:hasFunctionalCircuitRolePhenotype nposimtest:hasFunctionalCircuitRolePhenotype)
      }
      ?s owl:equivalentClass [
                               rdf:type owl:Class ;
                               owl:intersectionOf ?bn0
                             ] .
      ?bn0 rdf:rest*/rdf:first ilxtr:NeuronApinatSimple .
      ?bn0 rdf:rest*/rdf:first [
                                 rdf:type owl:Restriction ;
                                 owl:onProperty ?p ;
                                 owl:someValuesFrom ?o ]
    }}
  order by ?s ?np ?o
  limit 5000
}
#PREFIX nposimtest: <http://uri.interlex.org/tgbugs/uris/readable/npo-simple-test-1/>
select
distinct ?s ?np ?o
where
{
  {
    values (?p ?np) {
      (ilxtr:hasAxonPresynapticElementIn        nposimtest:hasAxonPresynapticElementIn)
      (ilxtr:hasSomaLocatedIn                   nposimtest:hasSomaLocatedIn)
      #(ilxtr:hasSomaLocatedIn                   nposimtest:hasAxonLocatedIn) # add inferred axon locations (decided to exclude)
      (ilxtr:hasAxonLocatedIn                   nposimtest:hasAxonLocatedIn)
      (ilxtr:hasDendriteLocatedIn               nposimtest:hasDendriteLocatedIn)
      (ilxtr:hasAxonSensorySubcellularElementIn nposimtest:hasAxonSensorySubcellularElementIn)

      #(ilxtr:hasAxonPresynapticElementIn ilxtr:hasAxonTerminalLocation)
      #(ilxtr:hasSomaLocatedIn ilxtr:hasSomaLocation)
      #(ilxtr:hasAxonLocatedIn ilxtr:hasAxonLocation)
      #(ilxtr:hasDendriteLocatedIn ilxtr:hasDendriteLocation)
      #(ilxtr:hasAxonSensorySubcellularElementIn ilxtr:hasAxonSensoryLocation)
    }
    ?s owl:equivalentClass [
                             rdf:type owl:Class ;
                             owl:intersectionOf ?bn0
                           ] .
    ?bn0 rdf:rest*/rdf:first ilxtr:NeuronApinatSimple .
    ?bn0 rdf:rest*/rdf:first [
                               rdf:type owl:Restriction ;
                               owl:onProperty ?p ;
                               owl:someValuesFrom [
                                                    a owl:Restriction ;
                                                    owl:onProperty partOf: ;
                                                    owl:someValuesFrom ?o
                                                  ] ]

  }
  union
  {
    values (?p ?np) {
      (ilxtr:hasForwardConnectionPhenotype     nposimtest:hasForwardConnectionPhenotype)
      (ilxtr:hasFunctionalCircuitRolePhenotype nposimtest:hasFunctionalCircuitRolePhenotype)
    }
    ?s owl:equivalentClass [
                             rdf:type owl:Class ;
                             owl:intersectionOf ?bn0
                           ] .
    ?bn0 rdf:rest*/rdf:first ilxtr:NeuronApinatSimple .
    ?bn0 rdf:rest*/rdf:first [
                               rdf:type owl:Restriction ;
                               owl:onProperty ?p ;
                               owl:someValuesFrom ?o ]
  }}
order by ?s ?np ?o
limit 5000
where
{
  {
    values (?p ?np) {
      (ilxtr:hasAxonPresynapticElementIn        nposimtest:hasAxonPresynapticElementIn)
      (ilxtr:hasSomaLocatedIn                   nposimtest:hasSomaLocatedIn)
      #(ilxtr:hasSomaLocatedIn                   nposimtest:hasAxonLocatedIn) # add inferred axon locations (decided to exclude)
      (ilxtr:hasAxonLocatedIn                   nposimtest:hasAxonLocatedIn)
      (ilxtr:hasDendriteLocatedIn               nposimtest:hasDendriteLocatedIn)
      (ilxtr:hasAxonSensorySubcellularElementIn nposimtest:hasAxonSensorySubcellularElementIn)

      #(ilxtr:hasAxonPresynapticElementIn ilxtr:hasAxonTerminalLocation)
      #(ilxtr:hasSomaLocatedIn ilxtr:hasSomaLocation)
      #(ilxtr:hasAxonLocatedIn ilxtr:hasAxonLocation)
      #(ilxtr:hasDendriteLocatedIn ilxtr:hasDendriteLocation)
      #(ilxtr:hasAxonSensorySubcellularElementIn ilxtr:hasAxonSensoryLocation)
    }
    ?s owl:equivalentClass [
                             rdf:type owl:Class ;
                             owl:intersectionOf ?bn0
                           ] .
    ?bn0 rdf:rest*/rdf:first ilxtr:NeuronApinatSimple .
    ?bn0 rdf:rest*/rdf:first [
                               rdf:type owl:Restriction ;
                               owl:onProperty ?p ;
                               owl:someValuesFrom [
                                                    a owl:Restriction ;
                                                    owl:onProperty partOf: ;
                                                    owl:someValuesFrom ?o
                                                  ] ]

  }
  union
  {
    values (?p ?np) {
      (ilxtr:hasForwardConnectionPhenotype     nposimtest:hasForwardConnectionPhenotype)
      (ilxtr:hasFunctionalCircuitRolePhenotype nposimtest:hasFunctionalCircuitRolePhenotype)
    }
    ?s owl:equivalentClass [
                             rdf:type owl:Class ;
                             owl:intersectionOf ?bn0
                           ] .
    ?bn0 rdf:rest*/rdf:first ilxtr:NeuronApinatSimple .
    ?bn0 rdf:rest*/rdf:first [
                               rdf:type owl:Restriction ;
                               owl:onProperty ?p ;
                               owl:someValuesFrom ?o ]
  }}

Sanity checks

Other queries run against various representations of the data.

JSON

jq -c '.datasets | length'
jq -c '[paths | select(.[-1] == "path_metadata")] | length'
function path-metadata-coverage () {
    local PATH_EXPORT_JSON=$1
    local COUNT_T=$(jq -c '.datasets | length' "${PATH_EXPORT_JSON}")
    local COUNT_PM=$(jq -c '[paths | select(.[-1] == "path_metadata")] | length' "${PATH_EXPORT_JSON}")
    awk -v t=${COUNT_T} -v pm=${COUNT_PM} 'BEGIN { print ( pm / t ) }'
}

General queries

General metadata and housekeeping queries.

List all loaded ontologies

SELECT ?s WHERE { ?s a owl:Ontology }
MATCH (o:Ontology) RETURN o

predicates

select distinct ?p ?l where { ?s ?p ?o . optional { ?p rdfs:label ?l . } } order by ?p
select ?s ?p ?o where {
values ?p { prov:startedAtTime prov:wasGeneratedBy dc:date owl:versionInfo TEMP:TimestampExportStart }
?s ?p ?o . } order by desc(str(?o))

versions

Useful version information is not present in all ontology files at the moment.

embedded load provenance record

SELECT ?p ?o WHERE {
build:prov ?p ?o .
}
MATCH
(p)-[i:build:id]-(),
(p)-[e]-()
RETURN i, e

curation-export.ttl

select ?s ?tes ?vi where {
  ?s TEMP:TimestampExportStart ?tes ;
     owl:versionInfo ?vi .
}

protcur.ttl

select distinct ?o where {
  <https://cassava.ucsd.edu/sparc/ontologies/protcur.ttl> owl:versionInfo ?o
}

apinat models

From #cypher-apinat-graphs

MATCH ({iri: "https://apinatomy.org/uris/elements/Graph"})<-[:type]-(g) RETURN g

Use cases

Connectivity dashboard

organs

from pyontutils.core import OntGraph

g = OntGraph().parse(data=input, format='ttl')

#return [[g.namespace_manager._qhrm(_)] for _ in sorted(set(g.subjects()))]
return ' '.join([g.namespace_manager._qhrm(_) for _ in sorted(set(g.subjects()))])
@prefix l: <http://www.w3.org/2000/01/rdf-schema#label> .
@prefix e: <http://uri.interlex.org/tgbugs/uris/readable/hasExistingId> .
@prefix FMA: <http://purl.org/sig/ont/fma/fma> .
@prefix UBERON: <http://purl.obolibrary.org/obo/UBERON_> .

# tissue
UBERON:0001013 l: "adipose tissue"; e: FMA:20110 .

# regions
UBERON:0002298 l: "brainstem"; e: FMA:79876 .
UBERON:0001016 l: "nervous system"; e: FMA:7157 .
UBERON:0000010 l: "peripheral nervous system"; e: FMA:9903 .
UBERON:0000013 l: "sympathetic nervous system"; e: FMA:9906 .
UBERON:0002005 l: "enteric nervous system"; e: FMA:66070 .
# TODO cardiovascular system # e.g. for blood pressure
# TODO cardiopulmonary system

# organs
UBERON:0000955 l: "brain"; e: FMA:50801 .
UBERON:0001155 l: "colon"; e: FMA:14543 .
UBERON:0002110 l: "gallbladder"; e: FMA:7202 .
UBERON:0000948 l: "heart"; e: FMA:7088 .
UBERON:0000160 l: "intestine"; e: FMA:7199 .
UBERON:0002113 l: "kidney"; e: FMA:7203 .
UBERON:0000059 l: "large intestine"; e: FMA:7201 .
UBERON:0002107 l: "liver"; e: FMA:7197 .
UBERON:0001556 l: "lower urinary tract"; e: FMA:45659 .
UBERON:0002048 l: "lung"; e: FMA:68877 .
UBERON:0001630 l: "muscle organ"; e: FMA:5022 .
UBERON:0001264 l: "pancreas"; e: FMA:7198 .
UBERON:0002097 l: "skin of body"; e: FMA:7163 .
UBERON:0002108 l: "small intestine"; e: FMA:7200 .
UBERON:0002240 l: "spinal cord"; e: FMA:7647 .
UBERON:0002106 l: "spleen"; e: FMA:7196 .
UBERON:0000945 l: "stomach"; e: FMA:7148 .
UBERON:0001255 l: "urinary bladder"; e: FMA:15900 .

# granular
UBERON:0000057 l: "urethra"; e: FMA:19667 .
UBERON:0001043 l: "esophagus"; e: FMA:7131 .

nerves

@prefix l: <http://www.w3.org/2000/01/rdf-schema#label> .
@prefix e: <http://uri.interlex.org/tgbugs/uris/readable/hasExistingId> .
@prefix FMA: <http://purl.org/sig/ont/fma/fma> .
@prefix UBERON: <http://purl.obolibrary.org/obo/UBERON_> .

# XXX sciatic nerve is missing from this list

# not actually cranial
UBERON:0002019 l: "accessory XI nerve"; e: FMA:6720 . # missing from the larger list

# nerves missing from the larger list
UBERON:0001646 l: "abducens nerve"; e: FMA:50867 .
UBERON:0001650 l: "hypoglossal nerve"; e: FMA:50871 .
UBERON:0001579 l: "olfactory nerve"; e: FMA:46787 .
UBERON:0001644 l: "trochlear nerve"; e: FMA:50865 .
UBERON:0003723 l: "vestibulocochlear nerve"; e: FMA:50869 .

# the larger list
UBERON:0034728 l: "autonomic nerve" .
UBERON:0009009 l: "carotid sinus nerve" .
UBERON:0009675 l: "chorda tympani branch of facial nerve" .
UBERON:0019198 l: "dorsal nerve of clitoris" .
UBERON:0019197 l: "dorsal nerve of penis" .
UBERON:0010380 l: "enteric nerve" .
UBERON:0010406 l: "cholinergic enteric nerve" .
UBERON:0001647 l: "facial nerve"; e: FMA:50868 .
UBERON:0001649 l: "glossopharyngeal nerve"; e: FMA:50870 .
UBERON:0001643 l: "oculomotor nerve"; e: FMA:50864 .
UBERON:0002924 l: "terminal nerve" .
UBERON:0001645 l: "trigeminal nerve"; e: FMA:50866 .
UBERON:0001759 l: "vagus nerve"; e: FMA:5731 .
# TODO vagus nerve branches
UBERON:0018680 l: "greater splanchnic nerve" .
UBERON:0003438 l: "iris nerve" .
UBERON:0011096 l: "lacrimal nerve" .
UBERON:0035642 l: "laryngeal nerve" .
UBERON:0011766 l: "left recurrent laryngeal nerve" .
UBERON:0011767 l: "right recurrent laryngeal nerve" .
UBERON:0003716 l: "recurrent laryngeal nerve" .
UBERON:0011326 l: "superior laryngeal nerve" .
UBERON:3010764 l: "laryngeus ventralis" .
UBERON:0001964 l: "least splanchnic nerve" .
UBERON:0018681 l: "lesser splanchnic nerve" .
UBERON:0003721 l: "lingual nerve" .
UBERON:0022301 l: "long ciliary nerve" .
UBERON:0018683 l: "lumbar splanchnic nerve" .
UBERON:0000377 l: "maxillary nerve" .
UBERON:0036143 l: "meningeal branch of mandibular nerve" .
UBERON:0017641 l: "meningeal branch of spinal nerve" .
UBERON:0022300 l: "nasociliary nerve" .
UBERON:0008810 l: "nasopalatine nerve" .
UBERON:0035650 l: "nerve of clitoris" .
UBERON:0035649 l: "nerve of penis" .
UBERON:0015162 l: "superior branch of oculomotor nerve" .
UBERON:0018675 l: "pelvic splanchnic nerve" .
UBERON:0011391 l: "perineal nerve" .
UBERON:0001884 l: "phrenic nerve" .
UBERON:0034725 l: "pterygopalatine nerve" .
UBERON:0011390 l: "pudendal nerve" .
UBERON:0009625 l: "sacral nerve" .
UBERON:0018684 l: "sacral splanchnic nerve" .
UBERON:0022302 l: "short ciliary nerve" .
UBERON:0003715 l: "splanchnic nerve" .
UBERON:0018679 l: "thoracic splanchnic nerve" .
UBERON:0001323 l: "tibial nerve" .
UBERON:0036216 l: "tympanic nerve" .
UBERON:0018412 l: "vidian nerve" .

# cervical nerve
UBERON:0000962 l: "nerve of cervical vertebra"; e: FMA:5859 .

plexi

UBERON:0002439 l: "myenteric nerve plexus"; e: FMA:63252 .

distributed ganglion that has not ensheathed there may be significant variablity between individuals in whether a plexius is fully ganglionated/ensheathed

the issue is that from the perspective the cell populations, they are actually the same population regardless of whether they are plexi or ganglia

maybe need to get these to converge onto the neuron populations so that we can merge the two

  • TODO consult with SAWG about these

ganglia

@prefix l: <http://www.w3.org/2000/01/rdf-schema#label> .
@prefix e: <http://uri.interlex.org/tgbugs/uris/readable/hasExistingId> .
@prefix syn: <http://uri.neuinfo.org/nif/nifstd/readable/synonym> .
@prefix FMA: <http://purl.org/sig/ont/fma/fma> .
@prefix UBERON: <http://purl.obolibrary.org/obo/UBERON_> .

UBERON:0001808 l: "parasympathetic ganglion" .
UBERON:0035776 l: "accessory ciliary ganglion" .
UBERON:0001805 l: "autonomic ganglion"; e: FMA:5889 .
UBERON:0014463 l: "cardiac ganglion" .
UBERON:0002262 l: "celiac ganglion" .
UBERON:0035783 l: "ganglion of ciliary nerve" .
UBERON:0001701 l: "glossopharyngeal ganglion" .
UBERON:0005360 l: "inferior glossopharyngeal IX ganglion" .
UBERON:0013500 l: "glossopharyngeal-vagus IX-X ganglion complex" .
UBERON:0035769 l: "mesenteric ganglion" .
UBERON:0005479 l: "superior mesenteric ganglion" .
UBERON:0005453 l: "inferior mesenteric ganglion" .
UBERON:0001989 l: "superior cervical ganglion" .
UBERON:0001990 l: "middle cervical ganglion" .
UBERON:0002440 l: "inferior cervical ganglion" .
UBERON:0003963 l: "otic ganglion" .
UBERON:0001807 l: "paravertebral ganglion" .
UBERON:0016508 l: "pelvic ganglion" .
UBERON:0003964 l: "prevertebral ganglion" .
UBERON:0003962 l: "pterygopalatine ganglion" .
UBERON:0005407 l: "sublingual ganglion" .
UBERON:0002059 l: "submandibular ganglion" .
UBERON:0001806 l: "sympathetic ganglion" .
UBERON:0005362 l: "vagus X ganglion" .
UBERON:0005364 l: "superior vagus X ganglion" .
UBERON:0005363 l: "inferior vagus X ganglion"; syn: "nodose ganglion" .
UBERON:0000408 l: "vertebral ganglion" .
UBERON:0002441 l: "cervicothoracic ganglion"; e: FMA:6469; syn: "stellate ganglion" .
  • TODO add synonyms

neuron population by organ

select distinct
?organ_start
?label
(count(distinct(?ele)) as ?elements)
#?elements
?model
#?ele
#?type
# ?sp
#?s
#?op_wat
#?p
#?o
where {
  values ?organ_start {
  nil
  nil
  nil
  }
  ?organ_start rdfs:label ?sl .
  bind(str(?sl) as ?label)
  # FIXME TODO traverse partonomy
  optional {
  values ?type { elements:Lyph elements:Group elements:Material elements:Chain }
  ?organ ( partOf: | rdfs:subClassOf )* ?organ_start .
  #?organ apinatomy:annotates ?ele .
  ?ele apinatomy:ontologyTerms ?organ .
  ?ele a ?type .
  ?model ( apinatomy:lyphs | apinatomy:chains | apinatomy:groups | apinatomy:materials ) ?ele .
  ?model a elements:Graph .
  #?ele apinatomy:inheritedOntologyTerms ?organ .
  #?o ?p ?q .
  }
}
group by ?organ_start ?label ?model
order by ?organ_start
#order by ?type ?organ ?model
#limit ?limit

./reports/organ-nerve-ganglia.csv

https://docs.google.com/spreadsheets/d/1pgprwTr4bvAaex8cNLeoRLUgt0VU5tilbWi4BCWF1JE/edit#gid=219324374 organ-nerve-ganglia

select distinct
?part_start
(str(?sl) as ?start_label)
where {
  ?part_start rdfs:subClassOf+/owl:someValuesFrom+ UBERON:0001255 .
  ?part_start rdfs:label ?sl .
}

the nested version here is massively slow

select distinct
?part
(str(?l) as ?label)
where {

  values ?part_0 { UBERON:0001255 }

  #?part_1 rdfs:label ?l1 .
  ?part_1 rdfs:subClassOf ?bn0 .
                          ?bn0 a owl:Restriction .
                          ?bn0 owl:onProperty partOf: .
                          ?bn0 owl:someValuesFrom ?part_0 .


  optional {
  #?part_2 rdfs:label ?l2 .
  ?part_2 rdfs:subClassOf ?bn1 .
                          ?bn1 a owl:Restriction .
                          ?bn1 owl:onProperty partOf: .
                          ?bn1 owl:someValuesFrom ?part_1 .

    optional {
    #?part_3 rdfs:label ?l3 .
    ?part_3 rdfs:subClassOf ?bn2 .
                            ?bn2 a owl:Restriction .
                            ?bn2 owl:onProperty partOf: .
                            ?bn2 owl:someValuesFrom ?part_2 .
    }

  }

  #optional {
  #?part_4 rdfs:label ?l4 .
  #?part_4 rdfs:subClassOf ?bn3 .
                          #?bn3 a owl:Restriction .
                          #?bn3 owl:onProperty partOf: .
                          #?bn3 owl:someValuesFrom ?part_3 .
  #}

  ?part a owl:Class .
  ?part rdfs:label ?l .
  filter ( ?part = ?part_0 || ?part = ?part_1 || ?part = ?part_2 || ?part = ?part_3 ) #( || ?part = ?part_4 )

}
limit ?limit
select distinct
?part_1
where {
values ?part_0 { UBERON:0009958 }

?part_1 rdfs:label ?l .
?part_1 rdfs:subClassOf ?bn0 .
                        ?bn0 a owl:Restriction .
                        ?bn0 owl:onProperty partOf: .
                        ?bn0 owl:someValuesFrom ?part_0 .
}
select distinct
?sigh
(str(?l) as ?label)
#?sigx
#(str(?lx) as ?labx)
where {
?sigh rdfs:subClassOf+/owl:someValuesFrom+ UBERON:0001255 . # consider also RO:0002433 usually go together
?sigh rdfs:label ?l .
# FIXME somehow male and female urethra are still being pulled in :/
#filter ( ?sigh != UBERON:0000057 )
#filter not exists { ?sigh rdfs:subClassOf/owl:someValuesFrom UBERON:0000057 . }
?sigh rdfs:subClassOf/owl:onProperty partOf: .
#?sigx rdfs:subClassOf ?bn .
                      #?bn a owl:Restriction .
                      #?bn owl:onProperty partOf: .
                      #?bn owl:someValuesFrom ?sigh .
#?sigx rdfs:label ?lx .

filter not exists { ?sigh rdfs:subClassOf/owl:onProperty <http://purl.obolibrary.org/obo/uberon/core#channels_into> . }
filter not exists { ?sigh rdfs:subClassOf/owl:onProperty <http://purl.obolibrary.org/obo/uberon/core#channels_from> . }
filter not exists { ?sigh rdfs:subClassOf/owl:onProperty BFO:0000051 . }
filter not exists { ?sigh rdfs:subClassOf/owl:onProperty RO:0002150 . }
filter not exists { ?sigh rdfs:subClassOf/owl:onProperty RO:0002221 . }
filter not exists { ?sigh rdfs:subClassOf/owl:onProperty RO:0002178 . }
}
order by ?label

select distinct
?part
?prop
(str(?l) as ?label)
?part_start
(str(?sl) as ?start_label)
where {
  values ?wat { UBERON:0001255 }
  # FIXME this is still completely broken because sparql and
  # transitive queries over over owl restrictions are ... problematic
  ?part_start rdfs:subClassOf+/owl:someValuesFrom+ ?wat .
  filter ( ?prop = partOf: )
  ?part_start rdfs:label ?sl .
  ?part_start rdfs:subClassOf ?bny .
                              ?bny owl:onProperty ?prop .
  ?part rdfs:label ?l .
  ?part rdfs:subClassOf ?bn0 .
                        ?bn0 a owl:Restriction .
                        ?bn0 owl:onProperty partOf: .
                        ?bn0 owl:someValuesFrom ?part_start .

}
order by ?start_label
from sparcur.sheets import Reports

class ONGRep:

    _data = data

    class options:
        preview = False
        to_sheets = True

    @Reports.makeReportSheet('organ_start', sheet_name='organ-nerve-ganglia')
    def populate(self, ext=None):
        return self._data, 'organ-nerve-ganglia coverage report'


t = ONGRep()
#t.populate()
return t._data

neuron population by nerve

neuron population by ganglion

devel

1 + 2
from pyontutils.core import OntTerm

asdf = ['ILX:0727875',
'ILX:0727396',
'ILX:0727235',
'ILX:0736783',
'ILX:0725331',
'ILX:0724284',
'ILX:0732483',
'ILX:0725803',
'ILX:0723837',
'ILX:0737036',
'ILX:0725086',
'ILX:0736097',
'ILX:0733375',
'ILX:0730739',
'ILX:0734588',
'ILX:0728741',
'ILX:0728157',
'ILX:0724431',
'ILX:0728322',
'ILX:0486534',
'ILX:0731053',
'ILX:0734339',
'ILX:0731969',
'ILX:0726449',
'ILX:0734582',
'ILX:0731914',
'ILX:0729870',
'ILX:0728987',
'ILX:0736712',
'ILX:0735649',
'ILX:0730231',
'ILX:0725969',
'ILX:0725552',
'ILX:0729045',
'ILX:0732611',
'ILX:0724152',
'ILX:0724140',
'ILX:0736057',
'ILX:0726733',
'ILX:0735212',
'ILX:0726145',
'ILX:0736880',
'ILX:0732740',
'ILX:0730942',
'ILX:0736020',
'ILX:0733193',
'ILX:0733995',
'ILX:0503992',
'ILX:0734328',
'ILX:0724764',
'ILX:0729794',
'ILX:0734607',
'ILX:0727602',
'ILX:0735024',
'ILX:0725520',
'ILX:0728778',
'ILX:0726312',
'ILX:0733021',
'ILX:0732970',
'ILX:0725326',
'ILX:0725956',
'ILX:0726454',
'ILX:0734766',
'ILX:0733700',
'ILX:0733004',
'ILX:0732328',
'ILX:0735011',
'ILX:0734057',
'ILX:0730509',
'ILX:0727725',
'ILX:0730966',
'ILX:0729052',
'ILX:0736358',
'ILX:0727970',
'ILX:0736369']

qq = [OntTerm(a) for a in asdf]
[(repr(_), _('ilxtr:hasIlxPreferredId')[0].curie) for _ in qq]

ApiNATOMY

Coverage of FMA by the TOO map.

Unclaimed regions from FMA/UBERON in the TOO map

Define the TOO map as the covering set of terms. It might not actually be, but that is how we will treat it. Probably want to avoid the immaterial entities.

Try to come up with a basis set from FMA as well.

If there are regional hierarchies that have been missed we will see them.

Summary statistics

  • debug
    SELECT distinct ?o
    WHERE {
      ?o a elements:Graph .
    } order by str(?o)
    
    select distinct
    ?graph
    ?lyphs
    ?label
    WHERE {
      # ?model apinatomy:hasGraph ?graph  # TODO broken conversion to literal instead of uri at the moment
    
      # sadly it seems that no one has managed to write an optimization that converts
      # into this form when querying multiple predicates in the same expression
      {
        ?graph a elements:Graph ;
               rdfs:label ?label .
        { select ?graph (count(distinct(?o)) as ?lyphs       ) where { ?graph apinatomy:lyphs        ?o . } group by ?graph }
      }
      union
      {
        { select (count(distinct(?graph_)) as ?graph  ) where { ?graph_ a elements:Graph } }
        bind("Total unique" as ?label)
        { select (count(distinct(?o)) as ?lyphs       ) where { ?graph_ apinatomy:lyphs        ?o . } }
    } }
    
  • Dashboard
    label graph lyphs lyphs_input externals publications nerus neupops neupops_pat somas
    ApiNATOMY model of the stomach sstom:sawg-stomach 668 37 77 8 14 14 14 14
    Ardell Armour Cardiac Model aacar:ard-arm-cardiac 1827 137 161 2 17 17 17 41
    Bolser-Lewis Model of the Physiology of the Superior Cervical Ganglion bolew:bolser-lewis 1151 107 141 239 29 29 29 29
    Keast ApiNATOMY model of bladder innervation kblad:keast-bladder 2394 139 174 30 21 20 20 35
    SAWG ApiNATOMY model of the descending colon sdcol:sawg-distal-colon 1066 89 135 5 18 18 18 24
    SPARC Bronchomotor Flatmap bromo:bronchomotor 623 144 187 6 6 6 6 19
    pancreas model pancr:pancreas 452 17 529 1 5 5 5 11
    ApiNATOMY model of the spleen splen:spleen 580 47 103 5 5 5 5 8
    Total unique 8 8761 717 823 296 115 114 114 181
    element count
    models 8
    models scigraph 8
    lyphs 8761
    lyphs input 717
    externals 583
    publications 296
    nerus 115
    neupops 114
    neupops pattern 114
    prefix count
    nlx 1
    CL 3
    GO 5
    NCIT 5
    CHEBI 11
    sao 11
    fma 15
    tgbugs 114
    ilx 179
    UBERON 239
    :SUM: 583
    :TOTAL: 583
    select distinct
    ?label
    ?graph
    #?g
    ?lyphs
    ?lyphs_input
    #?links
    #?nodes
    #?chains
    #?coalescences
    ?externals
    ?publications
    #?groups
    ?nerus
    ?neupops
    ?neupops_pat
    ?somas
    <<apinat-model-summary-where>>
    
    WHERE {
      # ?model apinatomy:hasGraph ?graph  # TODO broken conversion to literal instead of uri at the moment
    
      # sadly it seems that no one has managed to write an optimization that converts
      # into this form when querying multiple predicates in the same expression
      {
        ?graph a elements:Graph ;
               rdfs:label ?l .
        bind(str(?l) as ?label)  # avoid issues with graphs loaded multiple times
        { select ?graph (count(distinct(?o)) as ?lyphs       ) where { ?graph apinatomy:lyphs        ?o . } group by ?graph }
        { select ?graph (count(distinct(?o)) as ?lyphs_input ) where
          { ?graph apinatomy:lyphs ?o .
               filter not exists { ?o apinatomy:isTemplate true . }
               filter not exists { ?o apinatomy:generated  true . } }                                       group by ?graph }
        { select ?graph (count(distinct(?o)) as ?links       ) where { ?graph apinatomy:links        ?o . } group by ?graph }
        { select ?graph (count(distinct(?o)) as ?nodes       ) where { ?graph apinatomy:nodes        ?o . } group by ?graph }
        { select ?graph (count(distinct(?o)) as ?chains      ) where { ?graph apinatomy:chains       ?o . } group by ?graph }
        { select ?graph (count(distinct(?o)) as ?coalescences) where { ?graph apinatomy:coalescences ?o . } group by ?graph }
        { select ?graph (count(distinct(?o)) as ?externals   ) where { ?graph apinatomy:ontologyTerms     ?o . } group by ?graph }
        optional
        { select ?graph (count(distinct(?o)) as ?publications) where { ?graph apinatomy:references ?o . } group by ?graph }
        { select ?graph (count(distinct(?o)) as ?groups      ) where { ?graph apinatomy:groups       ?o . } group by ?graph }
        optional
        { select ?graph (count(distinct(?o)) as ?nerus       ) where
          { ?graph apinatomy:groups ?o .
                                    ?o apinatomy:description ?d .
                                         filter(contains(str(?d), "dynamic")) }                             group by ?graph }
        optional
        { select ?graph (count(distinct(?o)) as ?neupops     ) where
          { ?graph apinatomy:ontologyTerms ?o .
                                      ?o rdfs:subClassOf+ ilxtr:NeuronEBM . }                               group by ?graph }
        optional
        { select ?graph (count(distinct(?o)) as ?neupops_pat ) where
          { ?graph apinatomy:ontologyTerms ?o .
                  filter contains(str(?o), "/neuron-type-") }                                               group by ?graph }
        optional
        { select ?graph (count(distinct(?o)) as ?somas       ) where
          { ?graph apinatomy:lyphs ?o .
                                   ?o apinatomy:ontologyTerms NLX:154731 . }                                     group by ?graph }
      }
      union
      {
        { select (count(distinct(?graph_)) as ?graph  ) where { ?graph_ a elements:Graph } }
        bind("Total unique" as ?label)
        { select (count(distinct(?o)) as ?lyphs       ) where { ?graph_ apinatomy:lyphs        ?o . } }
        { select (count(distinct(?o)) as ?lyphs_input ) where
          { ?graph_ apinatomy:lyphs ?o .
               filter not exists { ?o apinatomy:isTemplate true . }
               filter not exists { ?o apinatomy:generated  true . } } }
        { select (count(distinct(?o)) as ?links       ) where { ?graph_ apinatomy:links        ?o . } }
        { select (count(distinct(?o)) as ?nodes       ) where { ?graph_ apinatomy:nodes        ?o . } }
        { select (count(distinct(?o)) as ?chains      ) where { ?graph_ apinatomy:chains       ?o . } }
        { select (count(distinct(?o)) as ?coalescences) where { ?graph_ apinatomy:coalescences ?o . } }
        { select (count(distinct(?o)) as ?externals   ) where { ?graph_ apinatomy:ontologyTerms     ?o . } }
        { select (count(distinct(?o)) as ?publications) where { ?graph_ apinatomy:references ?o . } }
        { select (count(distinct(?o)) as ?groups      ) where { ?graph_ apinatomy:groups       ?o . } }
        { select (count(distinct(?o)) as ?nerus       ) where
          { ?graph_ apinatomy:groups ?o .
                                     ?o apinatomy:description ?d .
                                          filter(contains(str(?d), "dynamic")) } }
        optional
        { select (count(distinct(?o)) as ?neupops     ) where
          { ?graph_ apinatomy:ontologyTerms ?o .
                                       ?o rdfs:subClassOf+ ilxtr:NeuronEBM . } }
        optional
        { select (count(distinct(?o)) as ?neupops_pat ) where
          { ?graph_ apinatomy:ontologyTerms ?o .
                   filter contains(str(?o), "/neuron-type-") } }
        optional
        { select (count(distinct(?o)) as ?somas       ) where
          { ?graph_ apinatomy:lyphs ?o .
                                    ?o apinatomy:ontologyTerms NLX:154731 . } }
      }
    }
    
  • Queries
    • ApiNATOMY sourced
      • models
        SELECT (COUNT(*) as ?count) {
          <<sparql-apinat-models>>
        }
        
        SELECT DISTINCT
        (?s as ?id)
        (str(?l) as ?label)
        WHERE {
          ?s a elements:Graph .
          ?s rdfs:label ?l.
        }
        
        SELECT DISTINCT
        ?o (str(?l) as ?label) (str(?v) as ?version)
        WHERE {
        ?o a owl:Ontology ;
           rdfs:label ?l ;
           apinatomy:hasGraph ?g .
        ?g a elements:Graph .
        ?g apinatomy:version ?v .
        }
        
        MATCH ({iri: "https://apinatomy.org/uris/elements/Graph"})<-[:type]-(g) RETURN g
        

        Return the list of all ApiNATOMY models in the database.

        MATCH ({iri: "https://apinatomy.org/uris/elements/Graph"})
        <-[:type]-(g)-[:isDefinedBy]->(o:Ontology)
        RETURN o
        
      • number of lyphs
        SELECT (COUNT(DISTINCT ?s) as ?total_lyphs)
        WHERE { ?s a elements:Lyph . }
        
        total_lyphs
        8162

        generated vs input s? rdf:type element:Lyph

        SELECT DISTINCT
        (COUNT(DISTINCT ?s) as ?input_lyphs)
        #?s
        #?p
        #?o
        WHERE {
          ?s a elements:Lyph .
          #?s ?p ?o .
          FILTER NOT EXISTS { ?s apinatomy:isTemplate true . }
          FILTER NOT EXISTS { ?s apinatomy:generated true . }
          #FILTER ( ! ( ?p = apinatomy:isTemplate && ?o = true ) )
          #FILTER ( ! ( ?p = apinatomy:generated && ?o = true ) )
          #?s apinatomy:generated ?o .
        }
        LIMIT ?limit
        
        input_lyphs
        742
      • publications referenced

        apinatomy:references average number of publications per unit? (for now no) will also want the full listing of publications select count distinct o? select distinct o? s? apinat:publication o?

        SELECT DISTINCT
        (COUNT(DISTINCT ?pub) as ?publications)
        WHERE { ?s apinatomy:references ?pub . }
        
        publications
        293
      • TODO species

        both model level and lyph level

      • terms employed in annotation

        count of apinatomy:ontologyTerms by prefix UBERON, GO, CHEBI, etc. full listing of terms as well

        "UBERON" "EMAPA" "fma" "CHEBI" "GO" "sao" "CL" "nlx" "ilx" "tgbugs" "NCBITaxon" "NCIT" "pubmed" "doi" "ncit"
        
        SELECT
        ?prefix
        (COUNT(DISTINCT ?s) AS ?count)
        WHERE {
          {
            VALUES ?prefix { "UBERON" "EMAPA" "fma" "CHEBI" "GO" "sao" "CL" "nlx" "ilx" "tgbugs" "NCBITaxon" "NCIT" "pubmed" "doi" "ncit" }
            ?s a elements:OntologyTerm .
            FILTER CONTAINS(str(?s), ?prefix)
          } UNION {
            SELECT (":SUM:" AS ?prefix) ?s
            {
              VALUES ?pre { "UBERON" "EMAPA" "fma" "CHEBI" "GO" "sao" "CL" "nlx" "ilx" "tgbugs" "NCBITaxon" "NCIT" "pubmed" "doi" "ncit" }
              ?s a elements:OntologyTerm .
              FILTER CONTAINS(str(?s), ?pre)
            }
          } UNION {
            BIND(":TOTAL:" AS ?prefix)
            { SELECT ?s WHERE { ?s a elements:OntologyTerm . } }
          }
        }
        GROUP BY ?prefix
        ORDER BY ?count
        
        SELECT (COUNT(DISTINCT(?s)) as ?count) WHERE { ?s a elements:OntologyTerm . }
        
        • external issues

          publications appearing as external ids, unmapped prefixes etc. FIXME where are these coming from?

          SELECT DISTINCT
          ?l ?s
          WHERE {
              ?s a elements:OntologyTerm .
              ?l ?p ?s .
              FILTER(CONTAINS(str(?s), "doi") || CONTAINS(str(?s), "pubmed") || CONTAINS(str(?s), "ncit"))
          }
          ORDER BY ?l ?s
          LIMIT ?limit
          
      • Number of neurulated groups

        Total neuron populations including those that are unidentified or are in error

        SELECT (COUNT(*) as ?count) {
          SELECT DISTINCT
          ?group
          (str(?l) as ?label)
          WHERE {
            ?group a elements:Group .
            ?group apinatomy:description ?d
            filter(contains(str(?d), "dynamic"))
            ?group rdfs:label ?l .
          }
          order by ?group
        }
        
        count
        117
        SELECT DISTINCT
        ?group
        (str(?l) as ?label)
        WHERE {
          ?group a elements:Group .
          ?group apinatomy:description ?d
          filter(contains(str(?d), "dynamic"))
          ?group rdfs:label ?l .
        }
        order by ?group
        
      • Number of neuron populations with external ids and correct subClassOf.
        SELECT (COUNT(*) as ?count) {
          SELECT DISTINCT
          ?neupop
          (str(?pl) as ?preferred_label)
          (str(?l) as ?label)
          WHERE {
            ?neupop a elements:OntologyTerm .
            ?neupop rdfs:subClassOf+ ilxtr:NeuronEBM .
            OPTIONAL { ?neupop skos:prefLabel ?pl . }
            OPTIONAL { ?neupop rdfs:label ?l . }
          }
        }
        
        count
        117
        SELECT DISTINCT
        ?neupop
        (str(?pl) as ?preferred_label)
        (str(?l) as ?label)
        WHERE {
          ?neupop a elements:OntologyTerm .
          ?neupop rdfs:subClassOf+ ilxtr:NeuronEBM .
          OPTIONAL { ?neupop skos:prefLabel ?pl . }
          OPTIONAL { ?neupop rdfs:label ?l . }
        }
        
      • Number of neuron populations with external ids that match the neuron-type pattern.
        SELECT (COUNT(*) as ?count) {
          SELECT DISTINCT
          ?neupop
          (str(?pl) as ?preferred_label)
          (str(?l) as ?label)
          WHERE {
            ?neupop a elements:OntologyTerm .
            FILTER CONTAINS(str(?neupop), "/neuron-type-")
            OPTIONAL { ?neupop skos:prefLabel ?pl . }
            OPTIONAL { ?neupop rdfs:label ?l . }
          }
        }
        
        count
        117
        SELECT DISTINCT
        ?neupop
        (str(?pl) as ?preferred_label)
        (str(?l) as ?label)
        WHERE {
          ?neupop a elements:OntologyTerm .
          FILTER CONTAINS(str(?neupop), "/neuron-type-")
          OPTIONAL { ?neupop skos:prefLabel ?pl . }
          OPTIONAL { ?neupop rdfs:label ?l . }
        }
        
    • NPO sourced queries

      Keast Bolser Lewis First pass NLP output

      • location

        experimental

        MATCH
        (neuron:Class{iri: "http://uri.neuinfo.org/nif/nifstd/sao1417703748"})<-[:subClassOf*1..]-
        (neupop)
        //(neupop:Class{iri: $neupop_id})
        -[e:ilxtr:hasLocationPhenotype!]->(blank)
        -[q]->(more)
        //-[q:BFO:0000050]->(more)
        RETURN e, q
        
      • phenotypes for neuron
        MATCH (neupop:Class{iri: $neupop_id})-[e:ilxtr:hasPhenotype!]->(phenotype) RETURN e
        
        from pprint import pprint
        from pyontutils.scigraph import Cypher, Vocabulary
        
        sgc = Cypher()
        query = """
        MATCH (neupop:Class{iri: $neupop_id})-[e:ilxtr:hasPhenotype!]->(phenotype) RETURN e
        """
        
        
        def get_phenotypes(neupop_id):
            d = sgc.execute(query=query, limit=99999, output='application/json', neupop_id=neupop_id)
            good = [e for e in d['edges'] if 'meta' in e and e['meta']['owlType'] == ['subClassOf']
                    and not e['obj'].startswith('_:')]
            return good
        
        
        pprint(get_phenotypes('ilxtr:neuron-type-keast-1'))
        
      • number of neuron populations aka neuron types
        SELECT DISTINCT
        ?s
        WHERE {
            ?s rdfs:subClassOf+ SAO:1813327414 . # NOTE this counts cells
        }
        
      • basic connectivity
        • python
          from collections import defaultdict
          from pprint import pprint
          from neurondm.compiled.common_usage_types import *
          neurons = config.neurons()
          #pprint(neurons)
          #pprint(dir(neurons[0]))
          # pprint(neurons[0].edges)
          #pprint([n for n in neurons if ilxtr.hasSomaLocatedIn in n.edges][0])
          #pprint([n for n in neurons if ilxtr.hasAxonLocatedIn in n.edges][0])
          #pprint([n for n in neurons if ilxtr.hasAxonPresynapticElementIn in n.edges][0])
          #pprint([n for n in neurons if ilxtr.hasDendriteLocatedIn in n.edges][0])
          
          x = defaultdict(list)
          for n in neurons:
              for somar in [pe.p for pe in n.pes if pe.e == ilxtr.hasSomaLocatedIn]:
                  for axonr in [pe.p for pe in n.pes if pe.e in (ilxtr.hasAxonPresynapticElementIn, ilxtr.hasProjectionPhenotype)]:
                      # via may be incorrect here if an axon location applies only to one collateral for a terminal
                      # this is why we need the partial order
                      x[somar].append(axonr)
          
          pprint(dict(x))
          
          # via axon
          pprint([n for n in neurons if ilxtr.hasAxonLocatedIn in n.edges][:1])
          
          # axon terminal in apinatomy
          # region A is axonally forward connect to B forward here means soma -> axon terminal
          pprint([n for n in neurons if ilxtr.hasProjectionPhenotype in n.edges][:1])
          pprint([n for n in neurons if ilxtr.hasAxonPresynapticElementIn in n.edges][:1])
          
          # soma
          pprint([n for n in neurons if ilxtr.hasSomaLocatedIn in n.edges][:1])
          pprint([n for n in neurons if ilxtr.hasSomaLocatedInLayer in n.edges][:1])
          
          # via dendrite
          pprint([n for n in neurons if ilxtr.hasDendriteLocatedIn in n.edges][:1])
          
          # dendrite terminal for sensory neurons in apinatomy
          # region A is dendritically forward connect to B forward here means dendrite terminal -> soma
          pprint([n for n in neurons if ilxtr.hasSensorySubcellularElementIn in n.edges][:1])
          
          pprint([n for n in neurons if ilxtr.hasForwardConnectionPhenotype in n.edges][:1])
          pprint([n for n in neurons if ilxtr.hasReverseConnectionPhenotype in n.edges][:1])
          
          #pprint([n for n in neurons if EntailedPhenotype('UBERON:0002790', 'ilxtr:hasAxonLocatedIn') in n.pes ])
          #pprint([n for n in neurons if ilxtr.hasSomaLocatedIn in n.edges])
          #pprint([n for n in neurons if Phenotype('UBERON:0002610', 'ilxtr:hasSomaLocatedIn') in n.pes ])
          
        • cypher
          :x a owl:Class ;
          owl:equivalentClass [ a owl:Restriction ;
          owl:intersectionOf (
          [ a owl:Restriction  ;
          owl:onProperty ilxtr:hasSomaLocatedIn ;
          owl:someValuesFrom [ a owl:Restriction ;
                         owl:onProperty :partOf ;
                         owl:someValuesFrom ?my-region ;
          
          ] ;
          ] ;
          )
          ]
          
          MATCH (neuron)-[e]->
          // (blank)-[:ilxtr:hasPhenotype]->(phenotype)
          (blanka)-[:ilxtr:hasAxonLocatedIn]->(blankb)-[edge]->(region_or_blank)
          //(blanka)-[:ilxtr:hasSomaLocatedIn]->(blankb)-[:BFO:0000050]->(region_or_blank)
          // or_blank happens if it is a union of regions i.e. a population with somas in more than one region
          
          //,(region_or_blank: Class{iri: "UBERON:0001950"})
          //RETURN region_or_blank
          //RETURN neuron
          RETURN edge
          
          select distinct ?p ?o
          where {
          # ilxtr:neuron-type-keast-1 ?p ?o
          ilxtr:neuron-type-keast-1 owl:equivalentClass ?b1 .
          ?b1 owl:intersectionOf ?b2 .
          ?b2 ?unkp ?b3 .
          # lists
          ?b3 owl:onProperty ?p .
          ?b3 owl:someValuesFrom ?o .
          }
          
          select distinct ?neuron ?prop ?value ?prop2 ?value2 # ?p ?ox # ?p2 ?o2 # ?p3 # ?o3 ?p4 ?o4 ?p5 ?o5 ?p6 ?o6
          where {
          #ilxtr:neuron-type-keast-1 owl:equivalentClass ?o .
          ?neuron a owl:Class .
          ?neuron ilxtr:simpleLocalLabel ?sll .  # XXX hack to filter for neurons
          ?neuron owl:equivalentClass ?o .
          # ?o ?p2 ?o2 .
          ?o owl:intersectionOf ?list .
          ?list rdf:rest*/rdf:first ?elem . # FIXME at least one elem subClassOf neuron or cell
          ?elem a owl:Restriction .
          ?elem owl:onProperty ?prop .
          ?elem owl:someValuesFrom ?value .
          OPTIONAL {
          ?value a owl:Restriction .
          ?value owl:onProperty ?prop2 .
          ?value owl:someValuesFrom ?value2 .
          # ?value ?p ?ox .
          #OPTIONAL {
          #?ox ?p2 ?o2 .
          #?o2 a owl:Class .
          #}
          }
          
          # then do optional for location vs other types
          
          #?o3 ?p4 ?o4 .
          #?o4 ?p5 ?o5 .  # first on property hits here
          #?o5 ?p6 ?o6 .
          # TODO list optional ???
          }
          limit 300
          
          MATCH path = (neuron)
          //MATCH path = (neuron:Class{iri: "ilxtr:neuron-type-keast-1"})
          -[:equivalentClass|subClassOf]->(hrm)
          RETURN path
          
      • number of distinct phenotypes
      • number of dimensions
      • number of phenotype values per phenotype dimension
        • number of anatomical regions
        • number of species
        • number of x

Date: 2022-12-21T23:47:11-05:00

Author: Tom Gillespie, Troy Sincomb

Created: 2022-12-22 Thu 01:38

Validate