Skip to content

Commit

Permalink
[SymForce] Misc docs updates
Browse files Browse the repository at this point in the history
Fix formatting of exception classes

Fixed some documentation of symbolic APIs, which was pretty limited and out
of date

Fixed dark mode code printing

Reviewers: michael-f,philipp,nathan,hayk,brad,ryan-b,chao
Topic: sf-docs
GitOrigin-RevId: edab4f42152a60843b436c910e727e80b1157bb4
  • Loading branch information
aaron-skydio committed Jan 9, 2024
1 parent 86efadf commit 5c0ed32
Show file tree
Hide file tree
Showing 13 changed files with 96 additions and 52 deletions.
8 changes: 1 addition & 7 deletions dev_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -356,20 +356,14 @@ traitlets==5.11.2
# nbconvert
# nbformat
# nbsphinx
types-docutils==0.20.0.3
# via types-pygments
types-jinja2==2.11.9
# via symforce (setup.py)
types-markupsafe==1.1.10
# via types-jinja2
types-pygments==2.16.0.0
# via symforce (setup.py)
types-requests==2.31.0.8
# via symforce (setup.py)
types-setuptools==68.2.0.0
# via
# symforce (setup.py)
# types-pygments
# via symforce (setup.py)
typing-extensions==4.8.0
# via
# astroid
Expand Down
14 changes: 14 additions & 0 deletions docs/static/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ section#symforce-home h1 {
* Font size tweaks
*/

/* Python */

/* h1 module names smaller */
section[id^="module-"] h1 {
font-size: 1.5em;
Expand All @@ -51,6 +53,18 @@ dl.py.class > dt.sig > span.sig-name {
font-size: 1.8em;
}

/* `exception` keywords larger */
dl.py.exception > dt.sig > em.property {
font-size: 1.3em;
}

/* exception names larger */
dl.py.exception > dt.sig > span.sig-name {
font-size: 1.8em;
}

/* C++ */

/* h1 class names smaller */
section[id^="class-"] h1 {
font-size: 1.5em;
Expand Down
6 changes: 0 additions & 6 deletions notebooks/tutorials/cameras_tutorial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@
"metadata": {},
"outputs": [],
"source": [
"# Setup\n",
"import symforce\n",
"\n",
"symforce.set_symbolic_api(\"sympy\")\n",
"symforce.set_log_level(\"warning\")\n",
"\n",
"import symforce.symbolic as sf\n",
"from symforce.notebook_util import display"
]
Expand Down
3 changes: 0 additions & 3 deletions notebooks/tutorials/codegen_tutorial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,8 @@
"\n",
"import symforce.symbolic as sf\n",
"from symforce import codegen\n",
"from symforce import ops\n",
"from symforce.codegen import codegen_util\n",
"from symforce.notebook_util import display\n",
"from symforce.notebook_util import display_code\n",
"from symforce.notebook_util import display_code_file\n",
"from symforce.values import Values"
]
Expand Down Expand Up @@ -136,7 +134,6 @@
")\n",
"\n",
"data = codegen_with_jacobians.generate_function()\n",
"from symforce.notebook_util import display_code_file\n",
"\n",
"display_code_file(data.generated_files[0], \"C++\")"
]
Expand Down
2 changes: 1 addition & 1 deletion notebooks/tutorials/ops_tutorial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"\n",
"There are three core concepts, each of which is a superset of the previous. The core routines use these ops interfaces rather than calling methods on types directly. The [ops package](../api/symforce.ops.html) docs provide much more detail and each op is tested on each type, but examples are given here.\n",
"\n",
"In the case of lists of objects or a Values object (see the [Values tutorial](../tutorials/values_tutorial.html)), we perform element-wise operations."
"In the case of different containers, such as lists, dataclasses, or Values objects (see the [Values tutorial](../tutorials/values_tutorial.html)), we perform operations recursively over their elements."
]
},
{
Expand Down
46 changes: 41 additions & 5 deletions notebooks/tutorials/sympy_tutorial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,41 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"SymForce is built on the SymPy API for symbolic algebra. If you're not familiar with SymPy or symbolic computation, go through [their](https://docs.sympy.org/latest/tutorial/index.html) tutorial.\n",
"SymForce is built on the [SymPy](https://sympy.org) API for symbolic algebra. If you're not familiar with SymPy or symbolic computation, go through [their](https://docs.sympy.org/latest/tutorial/index.html) tutorial.\n",
"Some basic usage is shown here."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"First, some optional configuration - see [Symbolic APIs](#Symbolic-APIs) below for more information:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Configuration (optional)\n",
"import symforce\n",
"\n",
"symforce.set_symbolic_api(\"sympy\")\n",
"symforce.set_log_level(\"warning\")\n",
"symforce.set_symbolic_api(\"symengine\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Some imports that are useful for notebooks - you'll see these across many of our tutorials:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from symforce.notebook_util import display\n",
"from symforce.notebook_util import print_expression_tree"
]
Expand All @@ -34,7 +54,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Always import the SymPy API through SymForce, because symforce can switch out the symbolic implementation of the API and adds a few minor but important augmentations. Let's define some algebraic symbols:"
"Always import the SymPy API through SymForce, because symforce can switch out the symbolic implementation of the API and adds a few minor but important augmentations. [symforce.symbolic](../api/symforce.symbolic.html) includes both the supported parts of the underlying symbolic API, as well as all of the types from [symforce.geo](../api/symforce.geo.html) and [symforce.cam](../api/symforce.cam.html). Let's define some algebraic symbols:"
]
},
{
Expand Down Expand Up @@ -122,6 +142,22 @@
"source": [
"display(sf.series(expr, y))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Symbolic APIs"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"SymForce supports two underling symbolic APIs. The [SymPy](https://sympy.org) API is pure Python, and the [SymEngine](https://symengine.org) API is a C++ implementation of much of SymPy. SymEngine is the default, and if you installed SymForce with `pip` or built it from source you'll have it already. SymEngine is much faster than SymPy by a factor of 100 or more. However, SymEngine doesn't implement everything SymPy does. SymForce abstracts over some of these differences, and adds support for some operations by converting between SymEngine and SymPy calls.\n",
"\n",
"For how to set the symbolic api, see the docs for [symforce.set_symbolic_api](../api/symforce.html#symforce.set_symbolic_api)."
]
}
],
"metadata": {
Expand Down
2 changes: 0 additions & 2 deletions notebooks/tutorials/values_tutorial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
"\n",
"import symforce.symbolic as sf\n",
"from symforce.notebook_util import display\n",
"from symforce.notebook_util import display_code\n",
"from symforce.notebook_util import display_code_file\n",
"from symforce.values import Values"
]
},
Expand Down
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ line-length = 100
exclude = ["third_party", "build", ".eggs"]
extend-include = ["*.ipynb"]

select = [
"I", # isort
"RUF100", # unused-noqa
]

[tool.ruff.lint.isort]
known-first-party = ["sym", "symforce"]
force-single-line = true
Expand Down
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,6 @@ def fixed_readme() -> str:
# NOTE(brad): A transitive dependency of pylint. Added here only to pin the version.
"lazy-object-proxy>=1.9.0",
"types-jinja2",
"types-pygments",
"types-requests",
"types-setuptools",
],
Expand Down
15 changes: 10 additions & 5 deletions symforce/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,18 @@ def _use_sympy() -> None:

def set_symbolic_api(name: str) -> None:
"""
Set the symbolic API for symforce. The sympy API is the default and pure python,
whereas the symengine API is C++ and requires building the symengine library. It can
be 100-200 times faster for many operations, but is less fully featured.
Set the symbolic API for symforce
The default is symengine if available else sympy, but can be set by one of:
See the SymPy tutorial for information on the symbolic APIs that can be used:
https://symforce.org/tutorials/sympy_tutorial.html
1) The SYMFORCE_SYMBOLIC_API environment variable
By default, SymForce will use the ``symengine`` API if it is available. If the symbolic API is
set to ``sympy`` it will use that. If ``symengine`` is not available and the symbolic API was
not set, it will emit a warning and use the ``sympy`` API.
The symbolic API can be set by one of:
1) The ``SYMFORCE_SYMBOLIC_API`` environment variable
2) Calling this function before any other symforce imports
Args:
Expand Down
38 changes: 20 additions & 18 deletions symforce/notebook_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
"""

import IPython
import pygments
import sympy as sympy_py

sympy_py.init_printing()
Expand All @@ -17,11 +16,18 @@
import symforce.symbolic as sf
from symforce import typing as T

if symforce.get_symbolic_api() == "symengine":
sf.sympy.init_printing()


def display(*args: T.Any) -> None:
"""
Display the given expressions in latex, or print if not an expression.
"""
# TODO(aaron): This should all be unnecessary on new symengine. The problem is that our version
# of symengine does not define `DenseMatrixBase._repr_latex_`, so we need to convert symengine
# matrices to sympy

if symforce.get_symbolic_api() == "sympy":
IPython.display.display(*args)
return
Expand All @@ -39,46 +45,42 @@ def display(*args: T.Any) -> None:
IPython.display.display(*args)


def display_code(code: str, language: str) -> None:
def display_code(code: str, language: str = None) -> None:
"""
Display code with syntax highlighting.
Args:
code (str): Source code
language (str): {python, c++, anything supported by pygments}
code: Source code
language: {python, c++, anything supported by pygments}
"""
# types-pygments doesn't have the type for this
lexer = T.cast(T.Any, pygments).lexers.get_lexer_by_name(language)

# And sometimes not this either
formatter = T.cast(T.Any, pygments).formatters.HtmlFormatter( # pylint: disable=no-member
noclasses=True
)
html = pygments.highlight(code, lexer, formatter)

IPython.display.display(IPython.display.HTML(html))
IPython.display.display(IPython.display.Code(code, language=language))


def display_code_file(path: T.Openable, language: str) -> None:
"""
Display code from a file path with syntax highlighting.
Args:
path (T.Openable): Path to source file
language (str): {python, c++, anything supported by pygments}
path: Path to source file
language: {python, c++, anything supported by pygments}
"""
with open(path) as f:
code = f.read()

display_code(code, language)


def print_expression_tree(expr: sf.Expr) -> None:
def print_expression_tree(expr: sf.Expr, assumptions: bool = False) -> None:
"""
Print a SymPy expression tree, ignoring node attributes
Args:
expr: The expression to print
assumptions: Whether to include assumption information for nodes. See
``sympy.printing.tree`` for more information.
"""
from sympy.printing.tree import tree

unfiltered_tree = tree(expr).split("\n")
unfiltered_tree = tree(expr, assumptions=assumptions).split("\n")
filtered_tree = "\n".join(v for i, v in enumerate(unfiltered_tree) if "+-" in v or i == 0)
print(filtered_tree)
2 changes: 1 addition & 1 deletion symforce/values/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def get_index_from_items(items: T.Iterable[T.Tuple[str, T.Any]]) -> T.Dict[str,
arbitrary names for each element in ``my_list``)
"""
offset = 0
index_dict = collections.OrderedDict()
index_dict = {}
for name, value in items:
entry_helper = lambda datatype=type(
value
Expand Down
6 changes: 3 additions & 3 deletions third_party/symenginepy/symengine/printing.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from symengine.lib.symengine_wrapper import ccode, sympify, Basic
import symengine.lib.symengine_wrapper
from symengine.lib import symengine_wrapper

class CCodePrinter:

Expand Down Expand Up @@ -29,6 +29,6 @@ def init_printing(pretty_print=True, use_latex=True):
if pretty_print:
if not use_latex:
raise RuntimeError("Only latex is supported for pretty printing")
symengine.lib.symengine_wrapper.repr_latex[0] = True
symengine_wrapper.repr_latex[0] = True
else:
symengine.lib.symengine_wrapper.repr_latex[0] = False
symengine_wrapper.repr_latex[0] = False

0 comments on commit 5c0ed32

Please sign in to comment.