--- title: Functions keywords: fastai sidebar: home_sidebar summary: "Functions that form the elements of a perceptual control node (system)." description: "Functions that form the elements of a perceptual control node (system)." nb_path: "nbs/02_functions.ipynb" ---
{% raw %}
{% endraw %} {% raw %}
%load_ext autoreload
%autoreload 2
{% endraw %} {% raw %}
#%nbdev_default_export functions
{% endraw %} {% raw %}
#%reload_ext autoreload
#%autoreload 2
{% endraw %}

Overview

Each function outputs the result of applying the function logic and may be a scalar or array, depending on the input. The inputs are supplied as links to another function, from where the values are read. Unless the function is a simple case, such as a 'Constant'.

Parameters are supplied as arguments to the constructor of the class.

All functions include the methods defined by the BaseFunction class.

{% raw %}
{% endraw %} {% raw %}

ControlUnitFunctions[source]

Enum = [PERCEPTION, REFERENCE, COMPARATOR, OUTPUT]

An enumeration.

{% endraw %} {% raw %}

CUF[source]

Enum = [PERCEPTION, REFERENCE, COMPARATOR, OUTPUT, ACTION]

An enumeration.

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class BaseFunction[source]

BaseFunction(name=None, value=None, links=None, new_name=True, namespace=None) :: ABC

Base class of a PCT function. This class is not used directly by developers, but defines the functionality common to all.

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class FunctionFactory[source]

FunctionFactory()

{% endraw %} {% raw %}
{% endraw %} {% raw %}
 
{% endraw %} {% raw %}
 
{% endraw %} {% raw %}

class Subtract[source]

Subtract(value=0, name='subtract', links=None, new_name=True, namespace=None, **cargs) :: BaseFunction

A function that subtracts one value from another. Parameter: None. Links: Two links required to each the values to be subtracted.

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class Proportional[source]

Proportional(gain=1, value=0, name='proportional', links=None, new_name=True, namespace=None, **cargs) :: BaseFunction

A proportion of the input value as defined by the gain parameter. Parameters: The gain value. Links: One.

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class Variable[source]

Variable(value=0, name='variable', links=None, new_name=True, namespace=None, **cargs) :: BaseFunction

A function that returns a variable value. Parameter: The variable value. Links: None

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class PassOn[source]

PassOn(value=0, name='variable', links=None, new_name=True, namespace=None, **cargs) :: BaseFunction

A function that passes on a variable value from a linked function. Parameter: None. Links: One

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class GreaterThan[source]

GreaterThan(threshold=0, upper=1, lower=0, value=0, name='greaterthan', links=None, new_name=True, namespace=None, **cargs) :: BaseFunction

One of two supplied values is returned if the input is greater than supplied threshold.</br> Parameters: The threshold and upper and lower value. Links: One

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class Constant[source]

Constant(value=0, name='constant', new_name=True, namespace=None, **cargs) :: BaseFunction

A function that returns a constant value. Parameter: The constant value. Links: None

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class Step[source]

Step(upper=None, lower=None, delay=None, period=None, value=0, name='step', new_name=True, namespace=None, **cargs) :: BaseFunction

A function that returns an alternating signal. Parameter: The upper and lower values, and a delay value. Links: None

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class Integration[source]

Integration(gain=1, slow=2, value=0, name='integration', links=None, new_name=True, namespace=None, **cargs) :: BaseFunction

A leaky integrating function. Equivalent of a exponential smoothing function, of the amplified input. Parameters: The gain and slow values. Links: One.

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class IntegrationDual[source]

IntegrationDual(gain=1, slow=2, value=0, name='integration', links=None, new_name=True, namespace=None, **cargs) :: BaseFunction

A leaky integrating function, applying one signal to another. Equivalent of a exponential smoothing function, of the amplified input. Parameters: The gain and slow values. Links: Two.

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class Sigmoid[source]

Sigmoid(range=2, scale=2, value=0, name='sigmoid', links=None, new_name=True, namespace=None, **cargs) :: BaseFunction

A sigmoid function. Similar to a proportional function, but kept within a limit (+/- half the range). Parameters: The range and scale (slope) values. Links: One.

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class WeightedSum[source]

WeightedSum(weights=[0], value=0, name='weighted_sum', links=None, new_name=True, usenumpy=False, namespace=None, **cargs) :: BaseFunction

A function that combines a set of inputs by multiplying each by a weight and then adding them up. Parameter: The weights array. Links: Links to all the input functions.

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class SmoothWeightedSum[source]

SmoothWeightedSum(weights=[0], smooth_factor=0, value=0, name='smooth_weighted_sum', links=None, new_name=True, usenumpy=False, namespace=None, **cargs) :: BaseFunction

A function that combines a set of inputs by multiplying each by a weight and then adding them up. And then smooths the result. Parameter: The weights array. Links: Links to all the input functions.

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class IndexedParameter[source]

IndexedParameter(index=None, value=0, name='indexed_parameter', links=None, new_name=True, namespace=None, **cargs) :: BaseFunction

A function that returns a parameter from a linked function, indexed by number. Parameter: The index. Links: One.

{% endraw %} {% raw %}
{% endraw %}

Creating Functions

Standard class constructor. Different ways to create a function with the standard constructor.

{% raw %}
prop = Proportional()
print(prop.get_config())
prop = Proportional("myprop", 10)
print(prop.get_config())
prop = Proportional(gain=10)
print(prop.get_config())
{'type': 'Proportional', 'name': 'proportional', 'value': 0, 'links': {}, 'gain': 1}
{'type': 'Proportional', 'name': 'proportional', 'value': 10, 'links': {}, 'gain': 'myprop'}
{'type': 'Proportional', 'name': 'proportional', 'value': 0, 'links': {}, 'gain': 10}
{% endraw %}

Configuration class constructor. Create the function by passing a configuration structure to the constructor.

{% raw %}
prop = Proportional(**{'name': 'myprop', 'value': 5, 'gain': 20})
print(prop.get_config())
{'type': 'Proportional', 'name': 'myprop', 'value': 5, 'links': {}, 'gain': 20}
{% endraw %}

Configuration class method. Create the function by passing a configuration structure to a class method.

{% raw %}
config = {'name': 'myprop', 'value': -0.5, 'gain': 21}
prop = Proportional.from_config(config)
print(prop.get_config())
{'type': 'Proportional', 'name': 'myprop', 'value': -0.5, 'links': {}, 'gain': 21}
{% endraw %} {% raw %}
prop = Proportional()
print(prop.get_config())
prop1 = Proportional.from_config(prop.get_config())
print(prop1.get_config())
assert prop.get_config() == prop1.get_config()
{'type': 'Proportional', 'name': 'proportional', 'value': 0, 'links': {}, 'gain': 1}
{'type': 'Proportional', 'name': 'proportional', 'value': 0, 'links': {}, 'gain': 1}
{% endraw %}

An example showing creating a WeightedSum function.

{% raw %}
wts=[1,1,1]
ws = WeightedSum(weights=wts)
ns = ws.namespace
ws.add_link(Constant(10, namespace=ns))
ws.add_link(Constant(5, namespace=ns))
ws.add_link(Constant(20, namespace=ns))
assert ws() == 35
config = ws.get_config()
{% endraw %} {% raw %}
ws1 = WeightedSum.from_config(config, namespace=ns)
ws1.get_config()
{'type': 'WeightedSum',
 'name': 'weighted_sum',
 'value': 35,
 'links': {0: 'constant', 1: 'constant1', 2: 'constant2'},
 'weights': [1, 1, 1]}
{% endraw %} {% raw %}
scons = Constant(2, name='scons')
sig = Sigmoid()
sig.add_link(scons)
sig()
0.7615941559557646
{% endraw %}

Viewing Functions

View the details of the function with the "summary", which prints the name, type, parameters, value and links (if any).

{% raw %}
prop.summary()
proportional Proportional | gain 1 | 0 
{% endraw %}

As already seen the function details can be seen by retrieving the configuration.

{% raw %}
print(prop.get_config())
{'type': 'Proportional', 'name': 'proportional', 'value': 0, 'links': {}, 'gain': 1}
{% endraw %}

Or you can print the function.

{% raw %}
print(prop)
{'namespace': UUID('4845871a-1ba9-11ec-a5c3-ffdcf36fa53d'), 'value': 0, 'links': [], 'checklinks': True, 'name': 'proportional', 'decimal_places': 3, 'gain': 1}
{% endraw %}

Set the decimal places for output display.

{% raw %}
print(prop.output_string())
prop.set_decimal_places(2)
print(prop.output_string())
0.000
0.00
{% endraw %}

You can also view a function graphically as a network of connected nodes.

{% raw %}
sub = Subtract(links=[Constant(1, name='cons'), Proportional(10, name='prop')], name='sub')
g = sub.graph()
print(g)
sub.draw(node_size=2000)

{% endraw %}

Running a Function

A function can be run simply by calling the variable.

{% raw %}
out = sub()
print(out)
1
{% endraw %}

A function can be also run in a loop with the run() method and provided the loop count.

{% raw %}
integrator = Integration(gain=9, slow=10)
integrator.add_link(sub)
o = integrator.run(steps=10, verbose=True)
0.900 1.710 2.439 3.095 3.686 4.217 4.695 5.126 5.513 5.862 
{% endraw %} {% raw %}
integrator(verbose=True)
6.176 
6.17570463519
{% endraw %}

Save and Load

Save a function to file.

{% raw %}
print(ws.get_config())
ws.save("ws.json")
{'type': 'WeightedSum', 'name': 'weighted_sum', 'value': 35, 'links': {0: 'constant', 1: 'constant1', 2: 'constant2'}, 'weights': [1, 1, 1]}
{% endraw %}

Create a function from file.

{% raw %}
wss = WeightedSum.load("ws.json", namespace=ns)
assert ws.get_config() == wss.get_config() 
print(wss.get_config())
{'type': 'WeightedSum', 'name': 'weighted_sum', 'value': 35, 'links': {0: 'constant', 1: 'constant1', 2: 'constant2'}, 'weights': [1, 1, 1]}
{% endraw %}

Setting Links

The next cell shows how a link is added to one function from another. In this case from an Integration function to a Constant function. So, whenever "integrator" runs it will get its input from "cons".

{% raw %}
integrator = Integration(3, 10)
cons = Constant(5)
integrator.add_link(cons)
integrator.summary()
integration Integration | gain 3 slow 10  | 0 | links  constant 
{% endraw %} {% raw %}
UniqueNamer.getInstance().clear() # initialises the list of function names
integ = Integration(**{'name': 'myinteg', 'value': 1, 'gain': 20, 'slow': 100})
prop = Proportional(5, name="myprop")
integ.add_link(prop)
print(integ.get_config())
assert integ.get_config() == {'type': 'Integration', 'name': 'myinteg', 'value': 1, 'links': {0: 'myprop'}, 'gain': 20, 'slow': 100}
{'type': 'Integration', 'name': 'myinteg', 'value': 1, 'links': {0: 'myprop'}, 'gain': 20, 'slow': 100}
{% endraw %}

You can also define the link when you create the function, as in this example with "Proportional".

{% raw %}
const = Constant(1, name='const')
ns = const.namespace
pr = Proportional(name='pr', links=const, namespace=ns)
pr.summary()
assert pr() == 1
pr Proportional | gain 1 | 0 | links  const 
{% endraw %}

It can be the name of the linked function.

{% raw %}
pr = Proportional(gain=10, name='pr', links='const', namespace=ns)
pr.summary()
assert pr() == 10
pr1 Proportional | gain 10 | 0 | links  const 
{% endraw %}

Or it can be a list of names.

{% raw %}
sub = Subtract(links=['const', 'pr'], namespace=ns)
sub.summary()
assert sub()==0
subtract Subtract | 0 | links  const pr 
{% endraw %}

Running a Fucntion

A function can simply be run by calling it, without any parameters. It will use whatever input was set by the links. It returns the result of the function. In this example it will be 5 * 3 / 10, that is, input * gain / slow.

{% raw %}
output = integrator()
print(output)
assert output == 1.5
1.5
{% endraw %} {% raw %}
print(integrator.get_config())
{'type': 'Integration', 'name': 'integration', 'value': 1.5, 'links': {0: 'constant'}, 'gain': 3, 'slow': 10}
{% endraw %}

Examples

Configuration

Create a function from the configuration of another.

{% raw %}
integrator = Integration(3, 10)
ns=integrator.namespace
cons = Constant(5, namespace=ns)
integrator.add_link(cons)
config = integrator.get_config()
inte = Integration.from_config(config, namespace=ns)
print(inte())
target = {'type': 'Integration', 'name': 'integration', 'value': 1.5, 'links': {0: 'constant'}, 'gain': 3, 'slow': 10}
#print(target)
assert inte.get_config() == target
1.5
{% endraw %}