Skip to content

Commit 14783e4

Browse files
committed
add more test units; refactor filter function; add IV support part1
1 parent 46f8baf commit 14783e4

10 files changed

Lines changed: 340 additions & 93 deletions

File tree

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# PythonVRFT Library - Version 0.0.4
1+
# PythonVRFT Library - Version 0.0.5
22
VRFT Adaptive Control Library written in Python. Aim of this library is to provide an implementation of the VRFT (Virtual Reference Feedback Tuning) algorithm.
33

44
You can find the package also at the following [link](https://pypi.org/project/pythonvrft/)
@@ -25,7 +25,7 @@ pip install .
2525
```
2626

2727
## Examples
28-
Examples are located in the examples/ folder. At the moment only 1 example is available.
28+
Examples are located in the examples/ folder. At the moment 2 examples are available. One without instrumental variables and one using instrumental variables.
2929

3030
## Tests
3131
To execute tests run the following command
@@ -34,11 +34,11 @@ python -m unittest
3434
```
3535

3636
## Changelog
37-
- [**DONE - V0.0.2**][26.03.2017] Implement the basic VRFT algorithm (1 DOF. offline, linear controller, controller expressed as scalar product theta*f(z))
38-
- [**DONE - V0.0.3**][05.01.2020] Code refactoring and conversion to Python 3; Removed support for Python Control library.
39-
- [**In Progress - V0.0.4**][07.01.2020-] Add Documentation and Latex formulas
37+
- [**DONE**][V. 0.0.2][26.03.2017] Implement the basic VRFT algorithm (1 DOF. offline, linear controller, controller expressed as scalar product theta*f(z))
38+
- [**DONE**][V. 0.0.3][05.01.2020] Code refactoring and conversion to Python 3; Removed support for Python Control library.
39+
- [**DONE**][v. 0.0.5][07.01.2020] Add Instrumental Variables (IVs) Support
40+
- [**In Progress**][-][07.01.2020-] Add Documentation and Latex formulas
4041
- [**TODO**] Add MIMO Support
41-
- [**TODO**] Add IV Support
4242
- [**TODO**] Generalize to other kind of controllers (e.g., neural nets)
4343

4444
## Citations

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77

88
setup(name = 'pythonvrft',
9-
version = '0.0.4',
9+
version = '0.0.5',
1010
description = 'VRFT Python Library',
1111
long_description = long_description,
1212
long_description_content_type = 'text/markdown',

test/test_iddata.py

Lines changed: 116 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# test_iddata.py - Unittest for the iddata object
2+
#
3+
# Code author: [Alessio Russo - alessior@kth.se]
4+
# Last update: 07th January 2020, by alessior@kth.se
5+
#
16
# Copyright [2017-2020] [Alessio Russo - alessior@kth.se]
27
# This file is part of PythonVRFT.
38
# PythonVRFT is free software: you can redistribute it and/or modify
@@ -10,33 +15,32 @@
1015
# You should have received a copy of the GNU General Public License
1116
# along with PythonVRFT. If not, see <http://www.gnu.org/licenses/>.
1217
#
13-
# Code author: [Alessio Russo - alessior@kth.se]
14-
# Last update: 06th January 2020, by alessior@kth.se
15-
#
18+
1619

1720
from unittest import TestCase
1821
from vrft.iddata import iddata
1922
import numpy as np
23+
import scipy.signal as scipysig
2024

2125
class TestIDData(TestCase):
2226
def test_type(self):
2327
a = iddata(0.0, 0.0, 0.0)
2428
with self.assertRaises(ValueError):
25-
a.checkData()
29+
a.check()
2630

2731
a = iddata(0.0, [1], 0.0)
2832
with self.assertRaises(ValueError):
29-
a.checkData()
33+
a.check()
3034

3135
a = iddata(np.zeros(10), 1, 0.0)
3236
with self.assertRaises(ValueError):
33-
a.checkData()
37+
a.check()
3438

3539
a = iddata([0 for i in range(10)], [0 for i in range(10)], 1.0)
36-
self.assertTrue(a.checkData())
40+
self.assertTrue(a.check())
3741

3842
a = iddata(np.zeros(10), np.zeros(10), 1.0)
39-
self.assertTrue(a.checkData())
43+
self.assertTrue(a.check())
4044

4145
def test_size(self):
4246
a = iddata(np.zeros(10), np.zeros(10), 0.0)
@@ -51,26 +55,124 @@ def test_size(self):
5155

5256
a = iddata(np.zeros(10), np.zeros(9), 0.0)
5357
with self.assertRaises(ValueError):
54-
a.checkData()
58+
a.check()
5559

5660
a = iddata(np.zeros(8), np.zeros(9), 0.0)
5761
with self.assertRaises(ValueError):
58-
a.checkData()
62+
a.check()
5963

6064

6165
def test_sampling_time(self):
6266
a = iddata(np.zeros(10), np.zeros(10), 0.0)
6367
with self.assertRaises(ValueError):
64-
a.checkData()
68+
a.check()
6569

6670
a = iddata(np.zeros(10), np.zeros(10), 1e-9)
6771
with self.assertRaises(ValueError):
68-
a.checkData()
72+
a.check()
6973

7074
a = iddata(np.zeros(10), np.zeros(10), -0.1)
7175
with self.assertRaises(ValueError):
72-
a.checkData()
76+
a.check()
77+
78+
a = iddata(np.zeros(10), np.zeros(10), 0.1)
79+
self.assertTrue(a.check())
7380

81+
def test_copy(self):
7482
a = iddata(np.zeros(10), np.zeros(10), 0.1)
75-
self.assertTrue(a.checkData())
83+
b = a.copy()
84+
self.assertTrue(a.check())
85+
self.assertTrue(b.check())
7686

87+
self.assertTrue(np.all(a.y == b.y))
88+
self.assertTrue(np.all(a.u == b.u))
89+
self.assertTrue(np.all(a.y0 == b.y0))
90+
self.assertTrue(a.ts == b.ts)
91+
92+
def test_filter(self):
93+
a = iddata(np.zeros(10), np.zeros(10), 0.1)
94+
L = scipysig.dlti([1], [1], dt=0.1)
95+
b = a.copy()
96+
a.filter(L)
97+
self.assertTrue(np.all(a.y == b.y))
98+
self.assertTrue(np.all(a.u == b.u))
99+
self.assertTrue(np.all(a.y0 == b.y0))
100+
self.assertTrue(a.ts == b.ts)
101+
102+
def test_split(self):
103+
n = 10
104+
a = iddata(np.random.normal(size=n), np.random.normal(size=n), 0.1)
105+
106+
b, c = a.split()
107+
108+
self.assertTrue(b.y.size == c.y.size)
109+
self.assertTrue(b.u.size == c.u.size)
110+
self.assertTrue(b.y0 == c.y0)
111+
self.assertTrue(b.ts == c.ts)
112+
self.assertTrue(b.ts == a.ts)
113+
self.assertTrue(np.all(b.y == a.y[:n//2]))
114+
self.assertTrue(np.all(b.u == a.u[:n//2]))
115+
self.assertTrue(b.y0 == a.y0)
116+
117+
self.assertTrue(np.all(c.y == a.y[n//2:]))
118+
self.assertTrue(np.all(c.u == a.u[n//2:]))
119+
self.assertTrue(c.y0 == a.y0)
120+
121+
y0 = [-1, 2]
122+
a = iddata(np.random.normal(size=n), np.random.normal(size=n), 0.1, y0)
123+
n0 = len(y0)
124+
n1 = (n + n0) // 2
125+
b, c = a.split()
126+
127+
self.assertTrue(b.y.size == c.y.size)
128+
self.assertTrue(b.u.size == c.u.size)
129+
self.assertTrue(b.ts == c.ts)
130+
self.assertTrue(b.ts == a.ts)
131+
self.assertTrue(np.all(b.y == a.y[:n1 - n0]))
132+
self.assertTrue(np.all(b.u == a.u[:n1 - n0]))
133+
self.assertTrue(np.all(b.y0 == a.y0))
134+
135+
self.assertTrue(np.all(c.y == a.y[n1:]))
136+
self.assertTrue(np.all(c.u == a.u[n1:]))
137+
self.assertTrue(np.all(c.y0 == a.y[n1 - n0:n1]))
138+
139+
140+
y0 = [-1, 2]
141+
n = 9
142+
a = iddata(np.random.normal(size=n), np.random.normal(size=n), 0.1, y0)
143+
n0 = len(y0)
144+
n -= 1
145+
n1 = (n + n0) // 2
146+
b, c = a.split()
147+
148+
self.assertTrue(b.y.size == c.y.size)
149+
self.assertTrue(b.u.size == c.u.size)
150+
self.assertTrue(b.ts == c.ts)
151+
self.assertTrue(b.ts == a.ts)
152+
self.assertTrue(np.all(b.y == a.y[:n1 - n0]))
153+
self.assertTrue(np.all(b.u == a.u[:n1 - n0]))
154+
self.assertTrue(np.all(b.y0 == a.y0))
155+
156+
self.assertTrue(np.all(c.y == a.y[n1:n]))
157+
self.assertTrue(np.all(c.u == a.u[n1:n]))
158+
self.assertTrue(np.all(c.y0 == a.y[n1 - n0:n1]))
159+
160+
y0 = [-1]
161+
n = 10
162+
a = iddata(np.random.normal(size=n), np.random.normal(size=n), 0.1, y0)
163+
n0 = len(y0)
164+
n -= 1
165+
n1 = (n + n0) // 2
166+
b, c = a.split()
167+
168+
self.assertTrue(b.y.size == c.y.size)
169+
self.assertTrue(b.u.size == c.u.size)
170+
self.assertTrue(b.ts == c.ts)
171+
self.assertTrue(b.ts == a.ts)
172+
self.assertTrue(np.all(b.y == a.y[:n1 - n0]))
173+
self.assertTrue(np.all(b.u == a.u[:n1 - n0]))
174+
self.assertTrue(np.all(b.y0 == a.y0))
175+
176+
self.assertTrue(np.all(c.y == a.y[n1:n]))
177+
self.assertTrue(np.all(c.u == a.u[n1:n]))
178+
self.assertTrue(np.all(c.y0 == a.y[n1 - n0:n1]))

test/test_reference.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
# Copyright [2017-2020] [Alessio Russo - alessior@kth.se]
1+
# test_reference.py - Unittest for virtual reference algorithm
2+
#
3+
# Code author: [Alessio Russo - alessior@kth.se]
4+
# Last update: 07th January 2020, by alessior@kth.se
5+
#
26
# This file is part of PythonVRFT.
37
# PythonVRFT is free software: you can redistribute it and/or modify
48
# it under the terms of the GNU General Public License as published by
@@ -10,9 +14,6 @@
1014
# You should have received a copy of the GNU General Public License
1115
# along with PythonVRFT. If not, see <http://www.gnu.org/licenses/>.
1216
#
13-
# Code author: [Alessio Russo - alessior@kth.se]
14-
# Last update: 06th January 2020, by alessior@kth.se
15-
#
1617

1718
from unittest import TestCase
1819
from vrft.iddata import *

test/test_utils.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# test_utils.py - Unittest for utilities
2+
#
3+
# Code author: [Alessio Russo - alessior@kth.se]
4+
# Last update: 07th January 2020, by alessior@kth.se
5+
#
16
# Copyright [2017-2020] [Alessio Russo - alessior@kth.se]
27
# This file is part of PythonVRFT.
38
# PythonVRFT is free software: you can redistribute it and/or modify
@@ -10,14 +15,13 @@
1015
# You should have received a copy of the GNU General Public License
1116
# along with PythonVRFT. If not, see <http://www.gnu.org/licenses/>.
1217
#
13-
# Code author: [Alessio Russo - alessior@kth.se]
14-
# Last update: 06th January 2020, by alessior@kth.se
15-
#
18+
1619

1720
from unittest import TestCase
1821
from vrft.utils import *
1922
from vrft.extended_tf import ExtendedTF
2023
from vrft.vrft_algo import virtualReference
24+
from vrft.iddata import iddata
2125
import numpy as np
2226
import scipy.signal as scipysig
2327

@@ -33,7 +37,7 @@ def test_deconvolve(self):
3337
y = y[:, 0]
3438
data = iddata(y, u, t_step, [0])
3539
r1, _ = virtualReference(data, sys.num, sys.den)
36-
r2 = deconvolve_signal(sys, data.y, data.ts)
40+
r2 = deconvolve_signal(sys, data.y)
3741
self.assertTrue(np.linalg.norm(r2-r1[:r2.size], np.infty) < 1e-3)
3842

3943

test/test_vrft.py

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# test_vrft.py - Unittest for VRFT
2+
#
3+
# Code author: [Alessio Russo - alessior@kth.se]
4+
# Last update: 07th January 2020, by alessior@kth.se
5+
#
16
# Copyright [2017-2020] [Alessio Russo - alessior@kth.se]
27
# This file is part of PythonVRFT.
38
# PythonVRFT is free software: you can redistribute it and/or modify
@@ -10,9 +15,7 @@
1015
# You should have received a copy of the GNU General Public License
1116
# along with PythonVRFT. If not, see <http://www.gnu.org/licenses/>.
1217
#
13-
# Code author: [Alessio Russo - alessior@kth.se]
14-
# Last update: 06th January 2020, by alessior@kth.se
15-
#
18+
1619

1720
from unittest import TestCase
1821
from vrft.iddata import *
@@ -37,13 +40,48 @@ def test_vrft(self):
3740
data = iddata(y,u,t_step,[0])
3841

3942
refModel = ExtendedTF([0.2], [1, -0.8], dt=t_step)
43+
prefilter = refModel * (1-refModel)
44+
45+
control = [ExtendedTF([1], [1,0], dt=t_step),
46+
ExtendedTF([1], [1,0,0], dt=t_step),
47+
ExtendedTF([1], [1,0,0,0], dt=t_step),
48+
ExtendedTF([1, 0], [1,1], dt=t_step)]
49+
50+
theta1, _, loss1, _ = compute_vrft(data, refModel, control, prefilter)
51+
theta2, _, loss2, _ = compute_vrft([data], refModel, control, prefilter)
52+
theta3, _, loss3, _ = compute_vrft([data, data], refModel, control, prefilter)
53+
54+
self.assertTrue(np.isclose(loss1, loss2))
55+
self.assertTrue(np.isclose(loss1, loss3))
56+
self.assertTrue(np.linalg.norm(theta1-theta2)<1e-15)
57+
self.assertTrue(np.linalg.norm(theta1-theta3)<1e-15)
58+
59+
def test_iv(self):
60+
t_start = 0
61+
t_end = 10
62+
t_step = 1e-2
63+
t = np.arange(t_start, t_end, t_step)
64+
u = np.ones(len(t)).tolist()
65+
66+
num = [0.1]
67+
den = [1, -0.9]
68+
sys = scipysig.TransferFunction(num, den, dt=t_step)
69+
t, y = scipysig.dlsim(sys, u, t)
70+
y = y.flatten() + np.random.normal(size=t.size)
71+
data = iddata(y,u,t_step,[0])
72+
73+
refModel = ExtendedTF([0.2], [1, -0.8], dt=t_step)
74+
prefilter = refModel * (1-refModel)
4075

4176
control = [ExtendedTF([1], [1,0], dt=t_step),
4277
ExtendedTF([1], [1,0,0], dt=t_step),
4378
ExtendedTF([1], [1,0,0,0], dt=t_step),
4479
ExtendedTF([1, 0], [1,1], dt=t_step)]
4580

46-
theta, _, _, loss, _ = compute_vrft(data, refModel, control, refModel * (1-refModel))
81+
##import pdb
82+
#pdb.set_trace()
83+
84+
# theta, _, loss, _ = compute_vrft(data, refModel, control, prefilter, iv=True)
4785

4886

4987

vrft/extended_tf.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def __add__(self,other):
7676
if type(other) in [int, float]:
7777
return ExtendedTF(polyadd(self.num, self.den*other), self.den, dt=self._dt)
7878
if type(other) in [TransFun, type(self)]:
79-
if np.all(self.den == other.den):
79+
if len(self.den) == len(other.den) and np.all(self.den == other.den):
8080
numer = polyadd(self.num, other.num)
8181
denom = self.den
8282
else:
@@ -88,7 +88,7 @@ def __sub__(self, other):
8888
if type(other) in [int, float]:
8989
return ExtendedTF(polyadd(self.num, -self.den*other), self.den, dt=self._dt)
9090
if type(other) in [TransFun, type(self)]:
91-
if np.all(self.den == other.den):
91+
if len(self.den) == len(other.den) and np.all(self.den == other.den):
9292
numer = polyadd(self.num, -other.num)
9393
denom = self.den
9494
else:
@@ -100,7 +100,7 @@ def __rsub__(self, other):
100100
if type(other) in [int, float]:
101101
return ExtendedTF(polyadd(-self.num, self.den*other), self.den, dt=self._dt)
102102
if type(other) in [TransFun, type(self)]:
103-
if np.all(self.den == other.den):
103+
if len(self.den) == len(other.den) and np.all(self.den == other.den):
104104
numer = polyadd(self.num, -other.num)
105105
denom = self.den
106106
else:

0 commit comments

Comments
 (0)