Skip to content

Commit

Permalink
Combine code for sparse model functions and their index files (#2159)
Browse files Browse the repository at this point in the history
Combine code for sparse model functions and their index files, i.e. generate only a single file instead of 3 individual files for content, rowvals, and colptrs, respectively.

Advantage: Faster import of smaller models and fewer files. For a toy model, this reduced the build steps from 44 to 28, and reduced build time by >20% on my computer.

Disadvantage: None found, so I don't think it worth adding an option for (not) combining those files. For larger models, there shouldn't be any impact. The extra time for compiling the index arrays should be negligible compared to computing the contents.

Related to #2119


Here a test for a large model (N=1):

| File         | Size    | Compilation time (s) |
|--------------|--------:|---------------------:|
| dwdx         | 22.4MiB |              3413.64 |
| dwdx_colptrs |  2.0KiB |                 2.79 |
| dwdx_rowvals | 65.6KiB |                 2.66 |
| *combined*   |         |              3416.79 |

I'd consider this time increase negligible.
  • Loading branch information
dweindl authored Aug 25, 2023
1 parent 8ead4b0 commit 98c782d
Showing 1 changed file with 27 additions and 21 deletions.
48 changes: 27 additions & 21 deletions python/sdist/amici/de_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
Set,
Tuple,
Union,
Literal
)

import numpy as np
Expand Down Expand Up @@ -2838,9 +2839,6 @@ def _generate_c_code(self) -> None:
if func_info.generate_body:
dec = log_execution_time(f"writing {func_name}.cpp", logger)
dec(self._write_function_file)(func_name)
if func_name in sparse_functions and func_info.body:
self._write_function_index(func_name, "colptrs")
self._write_function_index(func_name, "rowvals")

for name in self.model.sym_names():
# only generate for those that have nontrivial implementation,
Expand Down Expand Up @@ -3040,16 +3038,32 @@ def _write_function_file(self, function: str) -> None:
else:
equations = self.model.eq(function)

# function body
if function == "create_splines":
body = self._get_create_splines_body()
else:
body = self._get_function_body(function, equations)
if not body:
return

# colptrs / rowvals for sparse matrices
if function in sparse_functions:
lines = self._generate_function_index(function, "colptrs")
lines.extend(self._generate_function_index(function, "rowvals"))
lines.append("\n\n")
else:
lines = []

# function header
lines = [
lines.extend([
'#include "amici/symbolic_functions.h"',
'#include "amici/defines.h"',
'#include "sundials/sundials_types.h"',
"",
"#include <gsl/gsl-lite.hpp>",
"#include <algorithm>",
"",
]
])
if function == "create_splines":
lines += ['#include "amici/splinefunctions.h"', "#include <vector>"]

Expand Down Expand Up @@ -3096,14 +3110,6 @@ def _write_function_file(self, function: str) -> None:
]
)

# function body
if function == "create_splines":
body = self._get_create_splines_body()
else:
body = self._get_function_body(function, equations)
if not body:
return

if self.assume_pow_positivity and func_info.assume_pow_positivity:
pow_rx = re.compile(r"(^|\W)std::pow\(")
body = [
Expand Down Expand Up @@ -3137,16 +3143,20 @@ def _write_function_file(self, function: str) -> None:
with open(filename, "w") as fileout:
fileout.write("\n".join(lines))

def _write_function_index(self, function: str, indextype: str) -> None:
def _generate_function_index(
self, function: str, indextype: Literal["colptrs", "rowvals"]
) -> List[str]:
"""
Generate equations and write the C++ code for the function
``function``.
Generate equations and C++ code for the function ``function``.
:param function:
name of the function to be written (see ``self.functions``)
:param indextype:
type of index {'colptrs', 'rowvals'}
:returns:
The code lines for the respective function index file
"""
if indextype == "colptrs":
values = self.model.colptrs(function)
Expand Down Expand Up @@ -3233,11 +3243,7 @@ def _write_function_index(self, function: str, indextype: str) -> None:
]
)

filename = f"{function}_{indextype}.cpp"
filename = os.path.join(self.model_path, filename)

with open(filename, "w") as fileout:
fileout.write("\n".join(lines))
return lines

def _get_function_body(self, function: str, equations: sp.Matrix) -> List[str]:
"""
Expand Down

0 comments on commit 98c782d

Please sign in to comment.