-
Notifications
You must be signed in to change notification settings - Fork 149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Centralize code for creating new code generation backends #158
Conversation
Here's what it looks like to add a language after this review: #159 |
@@ -342,20 +342,19 @@ def get_formatted_list( | |||
formatted_symbols = [sm.Symbol(key)] | |||
flattened_value = [value] | |||
elif issubclass(arg_cls, geo.Matrix): | |||
if isinstance(config, codegen_config.PythonConfig): | |||
# TODO(nathan): Not sure this works for 2D matrices | |||
if config.matrix_is_1d: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I preserved the behavior, but don't fully understand it. Would like to discuss
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move this discussion into the IMU preintegration review (#160) where it is in play
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved in the other PRe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about the name "backends" for target languages, since we also use that for symbolic backends. Could call them "targets"?
symforce/codegen/codegen_config.py
Outdated
|
||
def printer(self) -> "sm.CodePrinter": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the correct annotation here is sympy.CodePrinter
, which mypy should be happy with without the string, and should indicate that they should subclass the sympy printer on either backend
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made it sympy. I think part of the reason this was in quotes was to avoid importing SymPy into the config files. We could import sympy in an if T.TYPE_CHECKING
block, but that's a bit ugly. Do you think we should do that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably fair to assume if you're importing the codegen config you're going to generate code, so you'll need to import sympy anyway - I'd lean towards just importing sympy here, and if there's a use case where that's annoyingly slow we can move it to a T.TYPE_CHECKING
block
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay I'm on board. It looks like it should be sympy.printing.codeprinter.CodePrinter
though?
@gcross-zipline since you're working on adding Rust bindings, please see this review which simplifies things. We should aim to rebase on top of this once it merges, so it looks like #159. |
It's a good callout. I feel like this use of backend is pretty similar to LLVM's terminology which makes me want to keep it. Target is fine, but I'm just having trouble getting behind it. I wonder if the sympy/symengine thing could be called something else like "get_sympy". Now that i think about it |
Yeah I'm down with calling these backends and renaming sympy/symengine, maybe call that |
Okay, doing that. |
a76176b
to
3715c7d
Compare
Move almost all backend-specific code into a sub-package of symforce/codegen/backends, rather than being scattered around multiple core files. This includes the code printer, codegen config, and a bunch of switch statements around the codegen machinery. The goal is to centralize and make it cleaner to add new codegen backends. These changes were made in parallel with adding a javascript backend, which will come in a follow-on review. See codegen/backends/README.md for the steps to add a backend after this review. There are still several ways to improve code quality and reorganize backend-specific code, but this is a step forward.
Because we are calling the codegen langauge targets "backends", it would be confusing to keep doing so with the SymPy / SymEngine switch. Backend matches LLVM terminology, so it makes sense there. We switch to using symbolic_api for the latter, which has some downsides in that we're setting the implementation and not the API, but it seems like the best choice of the options we've discussed.
3715c7d
to
5874834
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a couple of comments based on my experience adding basic rust code-gen support.
@@ -393,56 +394,23 @@ def generate_function( | |||
self.namespace = namespace | |||
|
|||
template_data = dict(self.common_data(), spec=self) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having the config be able to modify template_data
would be useful. For example, to expose utility methods that are specific to a given language: main...ZiplineTeam:add-rust-code-generation#diff-655b6a9779a72c1b59d1990ad1581a08986c7676a68d1f0c72a06018797e8c65R454
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eh, strange. In any case, it just adds another module to the template_data
dict like so:
import rust_util
template_data.update(rust_util=rust_util) # <- rust_util can be called from jinja now.
Giving different language CodegenConfig the ability to modify the data passed to jinja could be useful.
@@ -20,6 +24,7 @@ class CodegenConfig: | |||
use_eigen_types: Use eigen_lcm types for vectors instead of lists |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should use_eigen_types
exist on the base class, as it does not exist in other languages. (I realize this is unchanged by your PR, but it seemed relevant to the change).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It affects what we put in LCM definitions for generated LCM types regardless of target language (although of course the languages that currently support generated functions that take/return LCM types are currently just Python and C++ I think)
# NOTE(brad): The order of the symbols must match the storage order of geo.Matrix | ||
# (as returned by geo.Matrix.to_storage). Hence, if there storage order were | ||
# changed to, say, row major, the below for loops would have to be swapped to | ||
# reflect that. | ||
formatted_symbols = [] | ||
for j in range(value.shape[1]): | ||
for i in range(value.shape[0]): | ||
formatted_symbols.append(sm.Symbol(f"{key}({i}, {j})")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably not immediately important to you, but this indexing style also requires customization in some languages (eg. Rust). Could eventually have a method like format_data_accessor
for this as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 from me on that
Move almost all backend-specific code into a sub-package of
symforce/codegen/backends
, rather than being scattered around multiple core files. This includes the code printer, codegen config, and a bunch of switch statements around the codegen machinery. The goal is to centralize and make it cleaner to add new codegen backends. These changes were made in parallel with adding a javascript backend, which will come in a follow-on review.See this README for the steps to add a backend after this review.
There are still several ways to improve code quality and reorganize backend-specific code, but this is a step forward.
TODO: Regenerate all templates, since the template path changed (written to preamble).