def minreal(sys, tol=1e-8): """Pole/zero cancellation within a given tolerance. References: http://www.mathworks.com/help/control/ref/minreal.html """ z, p, k = sys2zpk(sys) mz = np.ones(len(z), dtype=bool) # start with all zeros mp = np.zeros(len(p), dtype=bool) # and no poles for i, pole in enumerate(p): # search among the remaining zeros bad = np.where((np.abs(pole - z) <= tol) & mz)[0] if len(bad): # cancel this pole with one of the zeros mz[bad[0]] = False else: # include this pole mp[i] = True return LinearSystem((z[mz], p[mp], k), analog=sys.analog)
def pole_zero_cancel(sys, tol=1e-8): """Pole/zero cancellation within a given tolerance. Sometimes referred to as the minimal realization in state-space. [#]_ This (greedily) finds pole-zero pairs within a given tolerance, and removes them from the transfer function representation. Parameters ---------- sys : :data:`linear_system_like` Linear system representation. tol : ``float``, optional Absolute tolerance to identify pole-zero pairs. Defaults to ``1e-8``. Returns ------- reduced_sys : :class:`.LinearSystem` Reduced linear system in zero-pole-gain form. References ---------- .. [#] http://www.mathworks.com/help/control/ref/minreal.html Examples -------- See :doc:`notebooks/research/linear_model_reduction` for a notebook example. >>> from nengolib.signal import pole_zero_cancel, s >>> sys = (s - 1) / ((s - 1) * (s + 1)) >>> assert pole_zero_cancel(sys) == 1 / (s + 1) """ z, p, k = sys2zpk(sys) mz = np.ones(len(z), dtype=bool) # start with all zeros mp = np.zeros(len(p), dtype=bool) # and no poles for i, pole in enumerate(p): # search among the remaining zeros bad = np.where((np.abs(pole - z) <= tol) & mz)[0] if len(bad): # cancel this pole with one of the zeros mz[bad[0]] = False else: # include this pole mp[i] = True return LinearSystem((z[mz], p[mp], k), analog=sys.analog)
def test_sys_conversions(): sys = Alpha(0.1) tf = sys2tf(sys) ss = sys2ss(sys) zpk = sys2zpk(sys) assert sys_equal(sys2ss(tf), ss) assert sys_equal(sys2ss(ss), ss) # unchanged assert sys_equal(sys2tf(tf), tf) # unchanged assert sys_equal(sys2tf(ss), tf) assert sys_equal(sys2zpk(zpk), zpk) # sanity check assert sys_equal(sys2zpk(tf), zpk) # sanity check assert sys_equal(sys2zpk(ss), zpk) assert sys_equal(sys2tf(zpk), tf) assert sys_equal(sys2ss(zpk), ss) # should also work with nengo's synapse types assert sys_equal(sys2zpk(nengo.Alpha(0.1)), zpk) assert sys_equal(sys2tf(nengo.Alpha(0.1)), tf) assert sys_equal(sys2ss(nengo.Alpha(0.1)), ss) # system can also be just a scalar assert sys_equal(sys2tf(2.0), (1, 0.5)) assert np.allclose(sys2ss(5)[3], 5) assert sys_equal(sys2zpk(5), 5) with pytest.raises(ValueError): sys2ss(np.zeros(5)) with pytest.raises(ValueError): sys2zpk(np.zeros(5)) with pytest.raises(ValueError): sys2tf(np.zeros(5)) with pytest.raises(ValueError): # _ss2tf(...): passthrough must be single element sys2tf(([], [], [], [1, 2]))