Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
127 commits
Select commit Hold shift + click to select a range
14de56c
Template finder
haneslinger Sep 12, 2023
f29fb92
Merge remote-tracking branch 'origin/develop' into template-finder
gtfierro Sep 16, 2023
53da375
Test with spruce
haneslinger Sep 18, 2023
2fbd6d6
Merge branch 'template-finder' of github.com:NREL/BuildingMOTIF into …
gtfierro Sep 19, 2023
2911504
making it easier to recover the actual param/token mapping from the o…
gtfierro Sep 20, 2023
025d042
format tokens
gtfierro Sep 20, 2023
e0fcd47
running notebook with new code
gtfierro Sep 20, 2023
056b4a3
add a more specific sa temp sensor entity
gtfierro Sep 22, 2023
38f3743
WIP
haneslinger Sep 28, 2023
9685913
updates from conversation 10/4
gtfierro Oct 4, 2023
8d65d72
Clean up
haneslinger Oct 9, 2023
98cacc4
Merge remote-tracking branch 'origin/develop' into template-finder
gtfierro Dec 7, 2023
c809ab9
aider: Add comments to the 'unify_bindings' function to explain what …
gtfierro Dec 7, 2023
e129d88
add comments to graph synthesizer components
gtfierro Dec 7, 2023
878a37d
update deps
gtfierro Dec 7, 2023
d4a2f68
Merge remote-tracking branch 'origin/develop' into template-finder
gtfierro Jul 3, 2024
3b533db
update brick
gtfierro Jul 4, 2024
a1ada86
Merge remote-tracking branch 'origin/develop' into template-finder
gtfierro Sep 1, 2024
5979465
move templates inside the graphsynethesizer object
gtfierro Sep 5, 2024
f9859f4
add example of semantic graph synthesis along with a new ingress and …
gtfierro Sep 18, 2024
8888c2b
remove dev folder
gtfierro Sep 18, 2024
82940bc
move some dev notebooks where they will not be tested
gtfierro Sep 18, 2024
a07d53c
Merge branch 'develop' into template-finder
gtfierro Sep 18, 2024
736ada9
Merge branch 'template-finder' of github.com:NREL/BuildingMOTIF into …
gtfierro Sep 18, 2024
9c07a75
poetry.lock
gtfierro Sep 18, 2024
ef11826
drop support for 3.9 for numpy 2.0?
gtfierro Sep 18, 2024
65842be
add SGS ingress
gtfierro Sep 18, 2024
4852762
fix kernel
gtfierro Sep 18, 2024
1988898
Merge branch 'develop' into template-finder
gtfierro Sep 18, 2024
a0bb134
remove dev notebooks
gtfierro Oct 29, 2024
f06b32a
moving files around
gtfierro Oct 29, 2024
a5dfc83
fix imports
gtfierro Oct 29, 2024
e7498a3
Merge branch 'develop' into template-finder
gtfierro Oct 30, 2024
2e06051
add CompiledModel class
gtfierro Nov 7, 2024
e870439
format code
gtfierro Nov 7, 2024
1cfd72a
fix variable names, python deps
gtfierro Nov 11, 2024
058fb2c
format
gtfierro Nov 18, 2024
6b8a019
Improve validation report output (#320)
gtfierro Nov 19, 2024
1a5cff9
Remove hardcoded namespaces and make them parameterized
gtfierro Nov 19, 2024
d4b45d7
Import Graph to resolve undefined name errors in synthesizer.py
gtfierro Nov 19, 2024
9f02958
Accept ontology as input argument in semantic graph synthesis methods
gtfierro Nov 19, 2024
5b1b738
factoring out the hardcoded Brick/NS values
gtfierro Nov 19, 2024
7c7e089
reformatting
gtfierro Nov 19, 2024
17b15c2
Merge branch 'develop' into template-finder
gtfierro Nov 19, 2024
5b41514
try to handle rolling back on library dep error?
gtfierro Nov 19, 2024
644827e
update poetry.lock
gtfierro Nov 19, 2024
4be13ca
try custom errors
gtfierro Nov 20, 2024
7b923db
add errors
gtfierro Nov 20, 2024
5cf0c78
add debug statement
gtfierro Nov 20, 2024
958a2f5
more debug statements
gtfierro Nov 20, 2024
89de0a6
add more debug
gtfierro Nov 20, 2024
ce1d680
default values for template error
gtfierro Nov 20, 2024
1bbb607
expand library errors
gtfierro Nov 22, 2024
3bf4a14
using my new errors
gtfierro Nov 22, 2024
72e91b8
fix exception name
gtfierro Nov 22, 2024
5b9a95b
try a new cascae delete on deps?
gtfierro Nov 22, 2024
e444e82
commit delete library on overwrite
gtfierro Nov 22, 2024
7ec1062
delete dependencies
gtfierro Nov 22, 2024
2024132
delete library directly
gtfierro Nov 22, 2024
8ddb49b
enable foreign keys in sqlite
gtfierro Nov 22, 2024
db1e695
CASCADE on delete
gtfierro Nov 22, 2024
a9c9f28
removing passive deletes
gtfierro Nov 22, 2024
c3fbed4
remove bad param
gtfierro Nov 22, 2024
70b010d
adjusting cascades and passives
gtfierro Nov 22, 2024
d638016
switch back to template clearing
gtfierro Nov 22, 2024
2990b71
add debug on sgs
gtfierro Nov 22, 2024
0c8265d
more log
gtfierro Nov 22, 2024
972f789
debug -> info
gtfierro Nov 22, 2024
5c32a81
parser needs a Constant for the first
gtfierro Nov 24, 2024
bdf578c
add comment
gtfierro Nov 24, 2024
a39dd28
print graph CBDs in the output
gtfierro Dec 2, 2024
da3418f
allow missing imports for validation
gtfierro Dec 2, 2024
007238f
add api endpoint to get template body
gtfierro Dec 2, 2024
5daeb7a
make it a GET request
gtfierro Dec 2, 2024
253d05d
typos
gtfierro Dec 2, 2024
5ce3233
add wrap combinator
gtfierro Dec 3, 2024
5de8c11
SGS does not do fill()
gtfierro Dec 3, 2024
2b44be7
clean up PARAM triples
gtfierro Dec 3, 2024
c1f88dd
fix use of shacl path
gtfierro Dec 3, 2024
6e7299e
remove context
gtfierro Dec 3, 2024
073293f
use pyshacl for manifest
gtfierro Dec 3, 2024
6f59c54
try to inline
gtfierro Dec 4, 2024
1ae16e0
fix query
gtfierro Dec 4, 2024
f525260
refactor: Move template inference logic from Library to ShapeCollection
gtfierro Dec 17, 2024
b40079c
fix: Resolve undefined names in shape_collection.py for template infe…
gtfierro Dec 17, 2024
25bab24
add docs on converting shapes to templates
gtfierro Dec 31, 2024
ff164c8
Merge remote-tracking branch 'origin/develop' into gtf-shape-to-table
gtfierro Feb 5, 2025
4fe6823
add compiled model in its own class, fix some deps
gtfierro Feb 5, 2025
4ee67dc
avoid subclassing Model
gtfierro Feb 5, 2025
6dcd455
Merge remote-tracking branch 'origin/develop' into gtf-shape-to-table
gtfierro Feb 7, 2025
3ca2222
python 3.10 thru 3.13
gtfierro Feb 7, 2025
93a56ce
no python 3.13 yet
gtfierro Feb 9, 2025
d59fd2e
Merge remote-tracking branch 'origin/develop' into gtf-shape-to-table
gtfierro Feb 25, 2025
632dca5
Merge branch 'gtf-shape-to-table' of github.com:NREL/BuildingMOTIF in…
gtfierro Feb 25, 2025
6dfe79f
udpate poetry from merge
gtfierro Feb 25, 2025
93b0ff4
Merge remote-tracking branch 'origin/develop' into template-finder
gtfierro Feb 25, 2025
03d7245
style
gtfierro Feb 25, 2025
964071e
bump pygit2
gtfierro Feb 25, 2025
bb57169
bump pygit2
gtfierro Feb 25, 2025
07e271b
adjsut versions
gtfierro Feb 25, 2025
adeb5f5
bumping versions, fixing poetry lock
gtfierro Feb 25, 2025
08ccef8
Merge branch 'gtf-shape-to-table' of github.com:NREL/BuildingMOTIF in…
gtfierro Feb 25, 2025
e5542a7
fix supported python versions
gtfierro Feb 25, 2025
09bf4f7
update precommit
gtfierro Feb 25, 2025
f5382e5
update flake8
gtfierro Feb 25, 2025
58be989
Merge remote-tracking branch 'origin/develop' into template-finder
gtfierro Feb 26, 2025
8eb2d43
Merge remote-tracking branch 'origin/develop' into gtf-shape-to-table
gtfierro Feb 26, 2025
5e96141
bump flake
gtfierro Feb 26, 2025
fd0989b
bump flake8
gtfierro Feb 26, 2025
de02b92
poetry lock
gtfierro Feb 26, 2025
513635d
changes for shacl path
gtfierro Mar 5, 2025
f1e9beb
remove snakeviz
gtfierro Mar 6, 2025
2e070a1
fixing test against shapes
gtfierro Mar 6, 2025
994558e
saving time on 223P validation
gtfierro Mar 7, 2025
2e83927
fixing notebooks
gtfierro Mar 7, 2025
7195815
Improving tests
gtfierro Mar 11, 2025
120b0de
Merge remote-tracking branch 'origin/develop' into template-finder
gtfierro Mar 19, 2025
9421b20
Merge branch 'template-finder' of github.com:NREL/BuildingMOTIF into …
gtfierro Mar 19, 2025
5acef1e
Merge remote-tracking branch 'origin/gtf-shape-to-table' into templat…
gtfierro Mar 19, 2025
fb33e19
Merge branch 'develop' into template-finder
gtfierro May 28, 2025
a89254b
undo changes to tables; handled by another PR
gtfierro Jun 3, 2025
424f627
refactor: Improve binding creation and query structure for clarity
gtfierro Jun 3, 2025
8b3f6c2
Merge remote-tracking branch 'origin/develop' into template-finder
gtfierro Jun 6, 2025
eecb10d
fix hashable param
gtfierro Jun 6, 2025
5f168f2
adding deps to templ
gtfierro Jun 9, 2025
5c5ee13
Merge branch 'develop' into template-finder
gtfierro Jul 2, 2025
f7feab3
Insure that token classes are sorted when constructing index label
TShapinsky Sep 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,4 @@ jobs:
- uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.xml
files: ./coverage.xml
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repos:
- id: black
entry: poetry run black
- repo: https://github.com/pycqa/flake8
rev: 7.0.0
rev: 7.1.2
hooks:
- id: flake8
entry: poetry run flake8 buildingmotif
Expand Down
5 changes: 3 additions & 2 deletions buildingmotif/dataclasses/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import rdflib
from pyshacl.helper.path_helper import shacl_path_to_sparql_path
from pyshacl.shapes_graph import ShapesGraph
from rdflib import Graph, URIRef
from rdflib.collection import Collection
from rdflib.term import BNode, Node
Expand Down Expand Up @@ -216,7 +217,7 @@ def reason(self) -> str:
"""Human-readable explanation of this GraphDiff."""
# interpret a SHACL property path as a sparql property path
path = shacl_path_to_sparql_path(
self.graph, self.path, prefixes=dict(self.graph.namespaces())
ShapesGraph(self.graph), self.path, prefixes=dict(self.graph.namespaces())
)

classname = self.graph.qname(self.classname)
Expand Down Expand Up @@ -394,7 +395,7 @@ def from_validation_report(cls, report: Graph) -> List["RequiredPath"]:
def reason(self) -> str:
"""Human-readable explanation of this GraphDiff."""
path = shacl_path_to_sparql_path(
self.graph, self.path, prefixes=dict(self.graph.namespaces())
ShapesGraph(self.graph), self.path, prefixes=dict(self.graph.namespaces())
)
return self.format_count_error(self.maxc, self.minc, path)

Expand Down
Empty file.
58 changes: 58 additions & 0 deletions buildingmotif/graph_generation/bindings_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import logging
from typing import List, Union

from rdflib import Graph, Namespace

from buildingmotif.dataclasses import Template
from buildingmotif.graph_generation.classes import Bindings, UnifiedBindings

logger = logging.getLogger()


def unify_bindings(bindings_list: List[Bindings]) -> List[UnifiedBindings]:
"""
Combine all the bindings for the same template with the same name.
"""
unified_bindings_list: List[UnifiedBindings] = []
for bindings in bindings_list:
if bindings.template is None:
continue

unified_bindings = next(
(
unified_bindings
for unified_bindings in unified_bindings_list
if unified_bindings.template.name == bindings.template.name
and unified_bindings.bindings["name"] == bindings.bindings["name"]
),
None,
)

if unified_bindings is None:
unified_bindings = UnifiedBindings(
labels=[],
template=bindings.template,
bindings={},
cost=bindings.cost,
)
unified_bindings_list.append(unified_bindings)

unified_bindings.labels.append(bindings.label)
unified_bindings.bindings.update(bindings.bindings)

return unified_bindings_list


def evaluate_bindings(
namespace: Namespace, bindings: Union[Bindings, UnifiedBindings]
) -> Union[Template, Graph]:
"""evaluate bindings"""
if bindings.template is None:
raise ValueError("bindings have no template.")

logger.debug(
f"Evaluating bindings for template {bindings.template.name}. Parameters: {bindings.bindings}"
)
return bindings.template.evaluate(
{p: namespace[t.identifier] for p, t in bindings.bindings.items()}
)
178 changes: 178 additions & 0 deletions buildingmotif/graph_generation/bipartite_token_mapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import logging
from typing import Dict, List, Optional, Tuple

import numpy as np
import pandas as pd
from rdflib import Graph
from scipy.optimize import linear_sum_assignment

from buildingmotif.dataclasses import Template
from buildingmotif.graph_generation.classes import Cost, Param, Token, URIRef
from buildingmotif.namespaces import PARAM

logger = logging.getLogger()


def get_typed_params(template) -> List[Param]:
"""
Get the parameters of the template that have a type
"""
query = """
SELECT ?s ?o
WHERE {
?s a ?o
FILTER (strstarts(str(?s), 'urn:___param___'))
}
"""
params = []
for s, c in template.body.query(query):
params.append(Param(name=s[len(PARAM) :], classname=c))
return params


class BipartiteTokenMapper:
@staticmethod
def _get_parent_class(ontology: Graph, ontology_class: URIRef) -> Optional[URIRef]:
"""Get the immediate parent of ontology class"""
query = """
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?parent
WHERE {{
?child rdfs:subClassOf ?parent .
}}
"""
result = ontology.query(query, initBindings={"child": ontology_class})
parents = (parent for (parent,) in result)

return next(parents, None)

@staticmethod
def _get_edge_cost(
ontology: Graph, token_class: URIRef, param_class: URIRef, cost_power: int = 0
) -> float:
"""
Return the cost between ontology classes token_class and param_class where cost is:

- inf if token_class is not covariant of param_class.
- 2 to the power of the number of hops between the classes.
"""
if str(token_class) == str(param_class):
return 2**cost_power - 1

parent_class = BipartiteTokenMapper._get_parent_class(ontology, token_class)
if parent_class is None:
return np.inf

return BipartiteTokenMapper._get_edge_cost(
ontology, parent_class, param_class, cost_power + 1
)

@staticmethod
def _create_cost_matrix(
ontology: Graph, tokens: List[Token], params: List[Param]
) -> pd.DataFrame:
"""Create cost matrix of the above classes."""

# a cost matrix is a matrix where the rows are the tokens and the columns are the params
cost_matrix = pd.DataFrame(
index=params,
columns=tokens,
)

# populate the cost matrix with the "distance" between each parameter class and
# each token class. The distance is the number of hops between the classes
# in the given ontology
for i, token in enumerate(cost_matrix.columns):
for j, param in enumerate(cost_matrix.index):
cost_matrix.iloc[j, i] = BipartiteTokenMapper._get_edge_cost(
ontology, token.classname, param.classname
)

logger.debug("cost matrix:")
logger.debug(
cost_matrix.rename(
columns=lambda x: x.class_,
index=lambda x: x.class_,
)
)
return cost_matrix

@staticmethod
def find_bindings_for_tokens_and_params(
ontology: Graph,
tokens: List[Token],
params: List[Param],
) -> Tuple[Dict[URIRef, Token], Cost]:
"""Get the cost of mapping token_classes to param_classes."""
# create cost matrix based on the distances between the classes of the tokens
# and the classes of the parameters. The params come from a template.
cost_matrix = BipartiteTokenMapper._create_cost_matrix(ontology, tokens, params)

# This code replaces all occurrences of positive infinity (np.inf) in
# the cost_matrix with NaN (Not a Number), drops any rows that are all
# NaN values, and finally replaces the remaining NaN values with
# positive infinity.
# The result is that the cost_matrix will have no rows that are all
# positive infinity, and no NaN values. This reduces the number of
# possible assignments that the linear_sum_assignment algorithm has to
# consider, and therefore reduces the runtime of the algorithm.

# TODO: determine if this is the right preprocessing step to take
cost_matrix = (
cost_matrix.replace([np.inf, -np.inf], np.nan)
.dropna(axis=0, how="all")
.replace(np.nan, np.inf)
)
# computes the optimal assignment between the tokens and the params
row_ind, col_ind = linear_sum_assignment(cost_matrix)
kept_costs = list(zip(row_ind, col_ind))
# turns the optimal assignment into a dictionary of bindings
bindings = {
cost_matrix.index[row].name: cost_matrix.columns[col]
for row, col in kept_costs
}

logger.debug("\nkept edges:")
for row, col in kept_costs:
param = cost_matrix.index[row]
token = cost_matrix.columns[col]
logger.debug(
f"{param.class_} <- {cost_matrix.iloc[row, col]} -> {token.class_}"
)

# if no edges, give the cost as infinity
if len(kept_costs) <= 0:
edge_cost = np.inf
else:
edge_cost = cost_matrix.to_numpy()[row_ind, col_ind].sum()

return (
bindings,
Cost(
edge_cost=edge_cost,
params_dropped=len(params) - len(kept_costs),
tokens_dropped=len(tokens) - len(kept_costs),
),
)

@staticmethod
def find_bindings_for_tokens_and_template(
ontology: Graph,
tokens: List[Token],
template: Template,
) -> Tuple[Dict[Param, Token], Cost]:
"""Finds the bindings for tokens and template"""
# get the parameters of the template that have a type
params = get_typed_params(template)
try:
mapping, cost = BipartiteTokenMapper.find_bindings_for_tokens_and_params(
ontology, tokens, params
)
except ValueError:
mapping, cost = {}, Cost(
edge_cost=np.inf,
params_dropped=len(params),
tokens_dropped=len(tokens),
)

return mapping, cost
Loading
Loading