Quick start guide ================= Installing the python package ----------------------------- Python wheels are available on `PyPi `_. Install the latest version of wrenfold with: .. code:: bash pip install wrenfold wrenfold is also available as a `conda-forge `_ package: .. code:: bash conda install -c conda-forge wrenfold **Alternatively**, python wheels may also be obtained from the `GitHub Releases Page `_. Select the ``whl`` file appropriate to your OS and python version. For example, for python 3.10 on arm64 OSX you would download and install ``wrenfold-0.2.1-cp310-cp310-macosx_11_0_arm64.whl``: .. code:: bash pip install wrenfold-0.2.1-cp310-cp310-macosx_11_0_arm64.whl Validating the install ---------------------- After installation, open a python REPL and test that wrenfold can be imported: .. code:: python import wrenfold print(wrenfold.__version__) # prints: 0.2.1 from wrenfold import sym x, y = sym.symbols('x, y') f = sym.cos(x * y) g = f.diff(x) print(g) # prints: -y * sin(x * y) Generating your first function ------------------------------ To illustrate the wrenfold workflow, we will generate a small example function and then invoke the generated code in C++ and Rust. To keep the code brief, we will choose something very simple for our example - the `Rosenbrock `_ function: .. math:: f\left(x, y\right) = \left(a - x\right)^2 + b\cdot\left(y - x^2\right)^2 First we express the function as a python function that manipulates wrenfold types: .. literalinclude:: quick_start_files/quick_start_script.py :language: python :start-after: function_def_start :end-before: function_def_end The argument type annotations let wrenfold know what dimensions and numerical types to expect for the input arguments to :math:`f`. Our python function returns two outputs: #. The first is the value of the Rosenbrock function, :math:`f`. We specify that this will be the *return value* of the generated C++ function. #. The second is the Jacobian of :math:`f`, taken with respect to :math:`\left[x, y\right]`, which will be an *output argument* of the generated C++ function. To generate some actual code, we run: .. literalinclude:: quick_start_files/quick_start_script.py :language: python :start-after: transpilation_start :end-before: transpilation_end Which produces: .. literalinclude:: quick_start_files/rosenbrock.h :language: cpp A couple of observations about this output: * The vector-valued input and output arguments were mapped to generic types. The generated function constructs n-dimensional spans in order to read/write to the vectors ``xy`` and ``f_D_xy``. * Terms that appear in both ``f`` and ``f_D_xy`` were extracted and shared between both outputs. * Our generated code depends on the `wrenfold runtime `_, a small header-only library that provides the :class:`wf::span` type. .. tip:: The runtime is also installed by the python wheel. It can be found at ``/include/site/python3.XX/wrenfold``. Calling the generated C++ ------------------------- Next, we will create a simple C++ program that evaluates our generated function. wrenfold functions can be made to work with :doc:`any dense matrix representation `. In this tutorial we will use `Eigen `_, as it is one of the most popular choices and wrenfold supports it out of the box. Our C++ file looks like: .. literalinclude:: quick_start_files/main.cpp :language: cpp To compile it we need to provide include paths for Eigen and the wrenfold runtime headers. You may need to adjust these paths for your system. .. code:: bash g++ -std=c++17 -I/usr/local/include/eigen3 -I/components/runtime main.cpp ./a.out And sure enough our test program outputs:: f = 0 f_D_xy = [0, 0] .. tip:: The wrenfold repo contains `a more complete version `_ of this example. Calling generated Rust ---------------------- By swapping out ``CppGenerator`` for ``RustGenerator``, we can emit rust code instead: .. literalinclude:: quick_start_files/quick_start_script.py :language: python :start-after: rust_transpilation_start :end-before: rust_transpilation_end Which produces: .. literalinclude:: quick_start_files/rosenbrock_crate/src/generated.rs :language: rust Like the C++ example, the generated rust code has a small runtime dependency. In this instance, it is a set of traits for constraining the input and output arguments. These traits are found in the `wrenfold-traits `_ crate. The ``wrenfold-traits`` crate includes default implementations for use with `nalgebra `_. To test our generated function, we will create a small test crate with the following ``Cargo.toml``: .. literalinclude:: quick_start_files/rosenbrock_crate/Cargo.toml :language: toml .. tip:: Note that we enabled ``features = ["nalgebra"]`` for the ``wrenfold-traits`` crate. And the following ``lib.rs``: .. literalinclude:: quick_start_files/rosenbrock_crate/src/lib.rs :language: rust