-
Notifications
You must be signed in to change notification settings - Fork 149
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
1 parent
fba4ae8
commit a56ade9
Showing
85 changed files
with
311 additions
and
221 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Code Generation Backends | ||
|
||
SymForce takes symbolic functions and generates runtime functions for multiple target backends. It is quite straightforward to add new backends. Before you do this, you should be familiar with [SymPy printing](https://docs.sympy.org/latest/modules/printing.html) for getting code from symbolic expressions, and with [Jinja templating](https://realpython.com/primer-on-jinja-templating/) for rendering output files. | ||
|
||
The minimal steps to support a new backend are: | ||
|
||
1. Choose a name for your backend (for example 'julia') and create a corresponding package in `symforce/codegen/backends`. | ||
2. Implement a subclass of `sympy.CodePrinter` that emits backend math code while traversing symbolic expressions. Sometimes SymPy already contains the backend and the best pattern is to inherit from it and customize as needed. The best way to do this is by looking at existing backends as examples. | ||
3. Implement a subclass of `symforce.codegen.codegen_config.CodegenConfig`. This is the spec that users pass to the `Codegen` object to use your backend. Again, see existing examples. Optionally import your config in `symforce/codegen/__init__.py`. | ||
4. Create a `templates` directory containing jinja templates that are used to generate the actual output files. They specify the high level structure and APIs around the math code. Your codegen config has a `templates_to_render` method that should match your templates. A typical start is just one function template. | ||
5. Add your backend's extensions to `FileType` in `symforce/codegen/template_util.py`, filling out relevant methods there. | ||
6. Add tests to `test/symforce_codegen_test.py`. | ||
|
||
This will result in being able to generate functions for your backend that deal with scalars and arrays, but the `sym` geometry and camera classes. To implement those, follow the examples in `geo_package_codegen` and `cam_package_codegen`. |
File renamed without changes.
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# ---------------------------------------------------------------------------- | ||
# SymForce - Copyright 2022, Skydio, Inc. | ||
# This source code is under the Apache 2.0 license found in the LICENSE file. | ||
# ---------------------------------------------------------------------------- | ||
from dataclasses import dataclass | ||
from pathlib import Path | ||
from sympy.printing.codeprinter import CodePrinter | ||
|
||
from symforce import typing as T | ||
from symforce.codegen.codegen_config import CodegenConfig | ||
|
||
CURRENT_DIR = Path(__file__).parent | ||
|
||
|
||
@dataclass | ||
class CppConfig(CodegenConfig): | ||
""" | ||
Code generation config for the C++ backend. | ||
Args: | ||
doc_comment_line_prefix: Prefix applied to each line in a docstring | ||
line_length: Maximum allowed line length in docstrings; used for formatting docstrings. | ||
use_eigen_types: Use eigen_lcm types for vectors instead of lists | ||
autoformat: Run a code formatter on the generated code | ||
cse_optimizations: Optimizations argument to pass to sm.cse | ||
support_complex: Generate code that can work with std::complex or with regular float types | ||
force_no_inline: Mark generated functions as `__attribute__((noinline))` | ||
zero_initialization_sparsity_threshold: Threshold between 0 and 1 for the sparsity below | ||
which we'll initialize an output matrix to 0, so we | ||
don't have to generate a line to set each zero | ||
element to 0 individually | ||
explicit_template_instantiation_types: Explicity instantiates templated functions in a `.cc` | ||
file for each given type. This allows the generated function to be compiled in its own | ||
translation unit. Useful for large functions which take a long time to compile. | ||
""" | ||
|
||
doc_comment_line_prefix: str = " * " | ||
line_length: int = 100 | ||
use_eigen_types: bool = True | ||
support_complex: bool = False | ||
force_no_inline: bool = False | ||
zero_initialization_sparsity_threshold: float = 0.5 | ||
explicit_template_instantiation_types: T.Optional[T.Sequence[str]] = None | ||
|
||
@classmethod | ||
def backend_name(cls) -> str: | ||
return "cpp" | ||
|
||
@classmethod | ||
def template_dir(cls) -> Path: | ||
return CURRENT_DIR / "templates" | ||
|
||
def templates_to_render(self, generated_file_name: str) -> T.List[T.Tuple[str, str]]: | ||
# Generate code into a header (since the code is templated) | ||
templates = [("function/FUNCTION.h.jinja", f"{generated_file_name}.h")] | ||
|
||
# Generate a cc file only if we need explicit instantiation. | ||
if self.explicit_template_instantiation_types is not None: | ||
templates.append(("function/FUNCTION.cc.jinja", f"{generated_file_name}.cc")) | ||
|
||
return templates | ||
|
||
def printer(self) -> CodePrinter: | ||
# NOTE(hayk): Is there any benefit to this being lazy? | ||
from symforce.codegen.backends.cpp import cpp_code_printer | ||
|
||
if self.support_complex: | ||
return cpp_code_printer.ComplexCppCodePrinter() | ||
else: | ||
return cpp_code_printer.CppCodePrinter() | ||
|
||
@staticmethod | ||
def format_data_accessor(prefix: str, index: int) -> str: | ||
return f"{prefix}.Data()[{index}]" |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# ---------------------------------------------------------------------------- | ||
# SymForce - Copyright 2022, Skydio, Inc. | ||
# This source code is under the Apache 2.0 license found in the LICENSE file. | ||
# ---------------------------------------------------------------------------- | ||
from dataclasses import dataclass | ||
from pathlib import Path | ||
from sympy.printing.codeprinter import CodePrinter | ||
|
||
from symforce import typing as T | ||
from symforce.codegen.codegen_config import CodegenConfig | ||
|
||
|
||
CURRENT_DIR = Path(__file__).parent | ||
|
||
|
||
@dataclass | ||
class PythonConfig(CodegenConfig): | ||
""" | ||
Code generation config for the Python backend. | ||
Args: | ||
doc_comment_line_prefix: Prefix applied to each line in a docstring | ||
line_length: Maximum allowed line length in docstrings; used for formatting docstrings. | ||
use_eigen_types: Use eigen_lcm types for vectors instead of lists | ||
autoformat: Run a code formatter on the generated code | ||
cse_optimizations: Optimizations argument to pass to sm.cse | ||
use_numba: Add the `@numba.njit` decorator to generated functions. This will greatly | ||
speed up functions by compiling them to machine code, but has large overhead | ||
on the first call and some overhead on subsequent calls, so it should not be | ||
used for small functions or functions that are only called a handfull of | ||
times. | ||
matrix_is_1D: geo.Matrix symbols get formatted as a 1D array | ||
""" | ||
|
||
doc_comment_line_prefix: str = "" | ||
line_length: int = 100 | ||
use_eigen_types: bool = True | ||
use_numba: bool = False | ||
matrix_is_1d: bool = True | ||
|
||
@classmethod | ||
def backend_name(cls) -> str: | ||
return "python" | ||
|
||
@classmethod | ||
def template_dir(cls) -> Path: | ||
return CURRENT_DIR / "templates" | ||
|
||
def templates_to_render(self, generated_file_name: str) -> T.List[T.Tuple[str, str]]: | ||
return [ | ||
("function/FUNCTION.py.jinja", f"{generated_file_name}.py"), | ||
("function/__init__.py.jinja", "__init__.py"), | ||
] | ||
|
||
def printer(self) -> CodePrinter: | ||
from symforce.codegen.backends.python import python_code_printer | ||
|
||
return python_code_printer.PythonCodePrinter() |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Empty file.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.