File: Differentiation.md

package info (click to toggle)
faust 2.81.10%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 431,496 kB
  • sloc: cpp: 283,941; ansic: 116,215; javascript: 18,529; sh: 14,356; vhdl: 14,052; java: 5,900; python: 5,091; objc: 3,852; makefile: 2,725; cs: 1,672; lisp: 1,146; ruby: 954; yacc: 586; xml: 471; lex: 247; awk: 111; tcl: 26
file content (121 lines) | stat: -rw-r--r-- 4,086 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# Differentation done at signal level

## Symbolic Differentiation

[Symbolic Differentation](https://en.wikipedia.org/wiki/Computer_algebra) is usually done using rewrite rules on expressions. We differentiate a Faust DSP program with respect to a given input variable (typically controllers like buttons, sliders, numentry). This is expressed using the `[diff:on]` metadata in the controller label.

## Implementation

- a Signal => Signal transformation pass named `SignalAutoDifferentiate` is implemented in `sigPromotion.hh` and `sigPromotion.cpp` files. For now only a subset of signals are actually handled: Integer and Real constants, `sin/cos/sqrt` unary functions, binary operators with special rules for `kMul`and `kDiv`.

- the `signalAutoDifferentiate` function uses a helper `DiffVarCollector` to collect the input variable for a given signal. The `SignalAutoDifferentiate` class is used to compute the derivated signal, which is added in a list of output signals, then compiled as usual, so code appears in the dsp `init/compute` method (this is the simplest way for now to see what happens). TODO: this obviously will have to be changed !

- the `signalAutoDifferentiate` pass is activated using the `-diff` compiler options.   

- since `SignalAutoDifferentiate`is a subclass of `SignalIdentity`, itself a subclass of `TreeTransform`, the differentiation itself takes profit of the memoïsation system already in place in the `TreeTransform` base class. Thus equal sub-expressions will be derivated once and shared in the transformation.

## Examples of generated code

Since the derivated signal is a regular signal, it takes benefit of all normalisation and optimisation (CSE, factorisation...) steps done in the `simplifyToNormalForm` function in `normalform.cpp`. 

The following program compiled with `faust -diff`:

```
process = 3*f1 + 5*f2
with {
    f1 = hslider("F1", 5, 0, 100, 0.1);
    f2 = hslider("F2 [diff:on]", 5, 0, 30, 0.1);
};
```

generates the C++ code:

```
virtual void compute(int count, 
    FAUSTFLOAT** RESTRICT inputs, 
    FAUSTFLOAT** RESTRICT outputs) {
    FAUSTFLOAT* output0 = outputs[0];
    for (int i0 = 0; i0 < count; i0 = i0 + 1) {
        output0[i0] = FAUSTFLOAT(5.0f);
    }
}
```

The following program compiled with `faust -diff`:

```
process = sin(5*f1)
with {
    f1 = hslider("F1 [diff:on]", 5, 0, 100, 0.1);
    f2 = hslider("F2", 5, 0, 30, 0.1);
};
```

generates the C++ code:

```
virtual void compute(int count, 
    FAUSTFLOAT** RESTRICT inputs, 
    FAUSTFLOAT** RESTRICT outputs) {
    FAUSTFLOAT* output0 = outputs[0];
    float fSlow0 = 5.0f * std::cos(5.0f * float(fHslider0));
    for (int i0 = 0; i0 < count; i0 = i0 + 1) {
        output0[i0] = FAUSTFLOAT(fSlow0);
    }
}
```

The following program compiled with `faust -diff`:

```
process = sin(cos(f2))
with {
    f1 = hslider("F1", 5, 0, 100, 0.1);
    f2 = hslider("F2 [diff:on]", 5, 0, 30, 0.1);
};
```

generates the C++ code:

```
virtual void compute(int count, 
    FAUSTFLOAT** RESTRICT inputs, 
    FAUSTFLOAT** RESTRICT outputs) {
    FAUSTFLOAT* output0 = outputs[0];
    float fSlow0 = float(fHslider0);
    float fSlow1 = std::cos(std::cos(fSlow0)) * (0.0f - std::sin(fSlow0));
    for (int i0 = 0; i0 < count; i0 = i0 + 1) {
        output0[i0] = FAUSTFLOAT(fSlow1);
    }
}
```

The following program compiled with `faust -diff`:

```
process = sin(cos(f2)) + cos(cos(f2))
with {
    f1 = hslider("F1", 5, 0, 100, 0.1);
    f2 = hslider("F2 [diff:on]", 5, 0, 30, 0.1);
};
```

generates the C++ code:

```
virtual void compute(int count, 
    FAUSTFLOAT** RESTRICT inputs, 
    FAUSTFLOAT** RESTRICT outputs) {
    FAUSTFLOAT* output0 = outputs[0];
    float fSlow0 = float(fHslider0);
    float fSlow1 = std::cos(fSlow0);
    float fSlow2 = (0.0f - std::sin(fSlow0)) 
        * (std::cos(fSlow1) 
        + (0.0f - std::sin(fSlow1)));
    for (int i0 = 0; i0 < count; i0 = i0 + 1) {
        output0[i0] = FAUSTFLOAT(fSlow2);
    }
}
```

The [following tool](https://www.matrixcalculus.org) can be used to check derivative computation.