Tutorial - Creating SQs from Vanilla Python Functions ===================================================== .. _tutorial-sqify: The first step in our application is to query our magnetometer to check if a car is near the sensor. Let's say the road is on a long highway where it is unexpected that the speed will change dramatically over 5 meters. We will model the inductance of our magnetometer with a negative cosine wave shifted vertically to avoid negative values. Our function for getting the magnetometer is shown below: .. code-block:: python def sample_magnetometer(A, f, phi): from math import cos, pi return A - (A * cos(get_time() * 2 * f / pi + phi))) Note that the ``sample_magnetometer`` has imports within the function body. These are necessary as SQs are not guaranteed to be on the same ensemble as its calling SQ, so each SQ requires its own environment and modules to be self-contained. We won't go into depth on ``get_time()``, but we'll assume ``get_time()`` will be responsible for generating increasing times so ``sample_magnetometer`` will produce different values over subsequent function calls. As stated before, ``sample_magnetometer`` has been provided by other developers for general use. The function itself is purely written in Python without any direct mention of TTPython constructs. The simplest way to include this Python function into TTPython is using the ``SQify`` function decorator around ``sample_magnetometer``, as shown below. .. code-block:: python @SQify def sample_magnetometer(A, f, phi): ... ``@SQify`` takes a Python function and inserts it as a SQ into our dataflow graph. The SQ defines the basic unit of computation in TTPython. The graph execution of our program is transparent to he programmer; they do not need to worry about converting data to tokens to communicate between SQ. When the SQ has the tokens it needs to run, it will provide the values in those tokens as arguments to the supplied function. A common error is to overuse ``@SQify`` over each separate function in an already defined Python program. This is unnecessary as ``@SQify`` treats the underlying wrapped function as a black box. The contained function should not have any mention of TTPython constructs. For example, .. code-block:: python @SQify def sample_magnetometer(A, f, phi): from math import cos, pi return A - (A * cos(get_time() * 2 * f / pi + phi))) # correct def get_time(): ... is correct, while .. code-block:: python @SQify def sample_magnetometer(A, f, phi): from math import cos, pi return A - (A * cos(get_time() * 2 * f / pi + phi))) @SQify # incorrect, SQified functions should not call other SQified functions def get_time(): ... incorrectly mixes the level of abstraction an SQ provides in TTPython. **NOTE**: ``@SQify`` limits the expressiveness of Python functions it decorates. ``*args`` is ont allowed in function definitions as our graph requires a statically known amount of inputs arguments. ``**kwargs`` are allowed in function calls if they are also defined within the function definition. We can now write a basic "Hello World" program to call ``sample_magnetometer`` once! We have informed TTPython how to include our sensing function as a SQ into our graph. Now, we need to define the graph structure for our program. To do so, we use another function decorator: ``@GRAPHify``. .. code-block:: python @SQify def sample_magnetometer(A, f, phi): from math import cos, pi return A - (A * cos(get_time() * 2 * f / pi + phi))) def get_time(): ... @GRAPHify def main(trigger): A = 1 f = 0.25 phi = 0 return sample_magnetometer(A, f, phi) The function decorator takes the following function as the main program to run in TTPython. The Python semantics in ``@GRAPHify`` are actually quite different from the regular sense. We'll discuss the nuances and restrictiosn of ``@GRAPHify`` later, but for now you can imagine that it behaves similarly as another Python function would. One immediate difference is that because our graphs require an input token to be called, there cannot be any parameterless function definitions. Futhermore, any function called within ``@GRAPHify`` needs to have been decorated by ``SQify``. **We need to show a few examples** * basic function being SQified * importing a file/module with an SQify'd function in it * keyword argument (must match identically, and should not be a variable (esp. not an arc)) * TT-keyword arguments for parameterizing TTPython primitives. Example with TTExecuteOnFullToken; READ_TTCLOCK is a good example in Instructions.py