Skip to content

Commit de3509e

Browse files
authored
Merge pull request #474 from sawyerbfuller/fix-connect
Fix bdalg.connect
2 parents 2d7aad0 + 9c5b5e4 commit de3509e

File tree

2 files changed

+98
-25
lines changed

2 files changed

+98
-25
lines changed

control/bdalg.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -302,14 +302,16 @@ def connect(sys, Q, inputv, outputv):
302302
sys : StateSpace Transferfunction
303303
System to be connected
304304
Q : 2D array
305-
Interconnection matrix. First column gives the input to be connected
306-
second column gives the output to be fed into this input. Negative
307-
values for the second column mean the feedback is negative, 0 means
308-
no connection is made. Inputs and outputs are indexed starting at 1.
305+
Interconnection matrix. First column gives the input to be connected.
306+
The second column gives the index of an output that is to be fed into
307+
that input. Each additional column gives the index of an additional
308+
input that may be optionally added to that input. Negative
309+
values mean the feedback is negative. A zero value is ignored. Inputs
310+
and outputs are indexed starting at 1 to communicate sign information.
309311
inputv : 1D array
310-
list of final external inputs
312+
list of final external inputs, indexed starting at 1
311313
outputv : 1D array
312-
list of final external outputs
314+
list of final external outputs, indexed starting at 1
313315
314316
Returns
315317
-------
@@ -325,15 +327,34 @@ def connect(sys, Q, inputv, outputv):
325327
>>> sysc = connect(sys, Q, [2], [1, 2])
326328
327329
"""
330+
inputv, outputv, Q = np.asarray(inputv), np.asarray(outputv), np.asarray(Q)
331+
# check indices
332+
index_errors = (inputv - 1 > sys.inputs) | (inputv < 1)
333+
if np.any(index_errors):
334+
raise IndexError(
335+
"inputv index %s out of bounds" % inputv[np.where(index_errors)])
336+
index_errors = (outputv - 1 > sys.outputs) | (outputv < 1)
337+
if np.any(index_errors):
338+
raise IndexError(
339+
"outputv index %s out of bounds" % outputv[np.where(index_errors)])
340+
index_errors = (Q[:,0:1] - 1 > sys.inputs) | (Q[:,0:1] < 1)
341+
if np.any(index_errors):
342+
raise IndexError(
343+
"Q input index %s out of bounds" % Q[np.where(index_errors)])
344+
index_errors = (np.abs(Q[:,1:]) - 1 > sys.outputs)
345+
if np.any(index_errors):
346+
raise IndexError(
347+
"Q output index %s out of bounds" % Q[np.where(index_errors)])
348+
328349
# first connect
329350
K = np.zeros((sys.inputs, sys.outputs))
330351
for r in np.array(Q).astype(int):
331352
inp = r[0]-1
332353
for outp in r[1:]:
333-
if outp > 0 and outp <= sys.outputs:
334-
K[inp,outp-1] = 1.
335-
elif outp < 0 and -outp >= -sys.outputs:
354+
if outp < 0:
336355
K[inp,-outp-1] = -1.
356+
elif outp > 0:
357+
K[inp,outp-1] = 1.
337358
sys = sys.feedback(np.array(K), sign=1)
338359

339360
# now trim

control/tests/bdalg_test.py

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import control as ctrl
1010
from control.xferfcn import TransferFunction
1111
from control.statesp import StateSpace
12-
from control.bdalg import feedback
12+
from control.bdalg import feedback, append, connect
1313
from control.lti import zero, pole
1414

1515
class TestFeedback(unittest.TestCase):
@@ -23,7 +23,9 @@ def setUp(self):
2323
# Two random SISO systems.
2424
self.sys1 = TransferFunction([1, 2], [1, 2, 3])
2525
self.sys2 = StateSpace([[1., 4.], [3., 2.]], [[1.], [-4.]],
26-
[[1., 0.]], [[0.]])
26+
[[1., 0.]], [[0.]]) # 2 states, SISO
27+
self.sys3 = StateSpace([[-1.]], [[1.]], [[1.]], [[0.]]) # 1 state, SISO
28+
2729
# Two random scalars.
2830
self.x1 = 2.5
2931
self.x2 = -3.
@@ -192,50 +194,50 @@ def testLists(self):
192194
sys1_2 = ctrl.series(sys1, sys2)
193195
np.testing.assert_array_almost_equal(sort(pole(sys1_2)), [-4., -2.])
194196
np.testing.assert_array_almost_equal(sort(zero(sys1_2)), [-3., -1.])
195-
197+
196198
sys1_3 = ctrl.series(sys1, sys2, sys3);
197199
np.testing.assert_array_almost_equal(sort(pole(sys1_3)),
198200
[-6., -4., -2.])
199-
np.testing.assert_array_almost_equal(sort(zero(sys1_3)),
201+
np.testing.assert_array_almost_equal(sort(zero(sys1_3)),
200202
[-5., -3., -1.])
201-
203+
202204
sys1_4 = ctrl.series(sys1, sys2, sys3, sys4);
203205
np.testing.assert_array_almost_equal(sort(pole(sys1_4)),
204206
[-8., -6., -4., -2.])
205207
np.testing.assert_array_almost_equal(sort(zero(sys1_4)),
206208
[-7., -5., -3., -1.])
207-
209+
208210
sys1_5 = ctrl.series(sys1, sys2, sys3, sys4, sys5);
209211
np.testing.assert_array_almost_equal(sort(pole(sys1_5)),
210212
[-8., -6., -4., -2., -0.])
211-
np.testing.assert_array_almost_equal(sort(zero(sys1_5)),
213+
np.testing.assert_array_almost_equal(sort(zero(sys1_5)),
212214
[-9., -7., -5., -3., -1.])
213215

214-
# Parallel
216+
# Parallel
215217
sys1_2 = ctrl.parallel(sys1, sys2)
216218
np.testing.assert_array_almost_equal(sort(pole(sys1_2)), [-4., -2.])
217219
np.testing.assert_array_almost_equal(sort(zero(sys1_2)),
218220
sort(zero(sys1 + sys2)))
219-
221+
220222
sys1_3 = ctrl.parallel(sys1, sys2, sys3);
221223
np.testing.assert_array_almost_equal(sort(pole(sys1_3)),
222224
[-6., -4., -2.])
223-
np.testing.assert_array_almost_equal(sort(zero(sys1_3)),
225+
np.testing.assert_array_almost_equal(sort(zero(sys1_3)),
224226
sort(zero(sys1 + sys2 + sys3)))
225-
227+
226228
sys1_4 = ctrl.parallel(sys1, sys2, sys3, sys4);
227229
np.testing.assert_array_almost_equal(sort(pole(sys1_4)),
228230
[-8., -6., -4., -2.])
229-
np.testing.assert_array_almost_equal(sort(zero(sys1_4)),
230-
sort(zero(sys1 + sys2 +
231+
np.testing.assert_array_almost_equal(sort(zero(sys1_4)),
232+
sort(zero(sys1 + sys2 +
231233
sys3 + sys4)))
232234

233-
235+
234236
sys1_5 = ctrl.parallel(sys1, sys2, sys3, sys4, sys5);
235237
np.testing.assert_array_almost_equal(sort(pole(sys1_5)),
236238
[-8., -6., -4., -2., -0.])
237-
np.testing.assert_array_almost_equal(sort(zero(sys1_5)),
238-
sort(zero(sys1 + sys2 +
239+
np.testing.assert_array_almost_equal(sort(zero(sys1_5)),
240+
sort(zero(sys1 + sys2 +
239241
sys3 + sys4 + sys5)))
240242
def testMimoSeries(self):
241243
"""regression: bdalg.series reverses order of arguments"""
@@ -270,6 +272,56 @@ def test_feedback_args(self):
270272
sys = ctrl.feedback(1, frd)
271273
self.assertTrue(isinstance(sys, ctrl.FRD))
272274

275+
def testConnect(self):
276+
sys = append(self.sys2, self.sys3) # two siso systems
277+
278+
# should not raise error
279+
connect(sys, [[1, 2], [2, -2]], [2], [1, 2])
280+
connect(sys, [[1, 2], [2, 0]], [2], [1, 2])
281+
connect(sys, [[1, 2, 0], [2, -2, 1]], [2], [1, 2])
282+
connect(sys, [[1, 2], [2, -2]], [2, 1], [1])
283+
sys3x3 = append(sys, self.sys3) # 3x3 mimo
284+
connect(sys3x3, [[1, 2, 0], [2, -2, 1], [3, -3, 0]], [2], [1, 2])
285+
connect(sys3x3, [[1, 2, 0], [2, -2, 1], [3, -3, 0]], [1, 2, 3], [3])
286+
connect(sys3x3, [[1, 2, 0], [2, -2, 1], [3, -3, 0]], [2, 3], [2, 1])
287+
288+
# feedback interconnection out of bounds: input too high
289+
Q = [[1, 3], [2, -2]]
290+
with self.assertRaises(IndexError):
291+
connect(sys, Q, [2], [1, 2])
292+
# feedback interconnection out of bounds: input too low
293+
Q = [[0, 2], [2, -2]]
294+
with self.assertRaises(IndexError):
295+
connect(sys, Q, [2], [1, 2])
296+
297+
# feedback interconnection out of bounds: output too high
298+
Q = [[1, 2], [2, -3]]
299+
with self.assertRaises(IndexError):
300+
connect(sys, Q, [2], [1, 2])
301+
Q = [[1, 2], [2, 4]]
302+
with self.assertRaises(IndexError):
303+
connect(sys, Q, [2], [1, 2])
304+
305+
# input/output index testing
306+
Q = [[1, 2], [2, -2]] # OK interconnection
307+
308+
# input index is out of bounds: too high
309+
with self.assertRaises(IndexError):
310+
connect(sys, Q, [3], [1, 2])
311+
# input index is out of bounds: too low
312+
with self.assertRaises(IndexError):
313+
connect(sys, Q, [0], [1, 2])
314+
with self.assertRaises(IndexError):
315+
connect(sys, Q, [-2], [1, 2])
316+
# output index is out of bounds: too high
317+
with self.assertRaises(IndexError):
318+
connect(sys, Q, [2], [1, 3])
319+
# output index is out of bounds: too low
320+
with self.assertRaises(IndexError):
321+
connect(sys, Q, [2], [1, 0])
322+
with self.assertRaises(IndexError):
323+
connect(sys, Q, [2], [1, -1])
324+
273325

274326
if __name__ == "__main__":
275327
unittest.main()

0 commit comments

Comments
 (0)