{"action":"created","issue":{"url":"https://api.github.com/repos/nengo/nengo/issues/938","labels_url":"https://api.github.com/repos/nengo/nengo/issues/938/labels{/name}","comments_url":"https://api.github.com/repos/nengo/nengo/issues/938/comments","events_url":"https://api.github.com/repos/nengo/nengo/issues/938/events","html_url":"https://github.com/nengo/nengo/issues/938","id":129097103,"number":938,"title":"Off-by-one in LinearFilter.General step","user":{"login":"arvoelke","id":5420057,"avatar_url":"https://avatars.githubusercontent.com/u/5420057?v=3","gravatar_id":"","url":"https://api.github.com/users/arvoelke","html_url":"https://github.com/arvoelke","followers_url":"https://api.github.com/users/arvoelke/followers","following_url":"https://api.github.com/users/arvoelke/following{/other_user}","gists_url":"https://api.github.com/users/arvoelke/gists{/gist_id}","starred_url":"https://api.github.com/users/arvoelke/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/arvoelke/subscriptions","organizations_url":"https://api.github.com/users/arvoelke/orgs","repos_url":"https://api.github.com/users/arvoelke/repos","events_url":"https://api.github.com/users/arvoelke/events{/privacy}","received_events_url":"https://api.github.com/users/arvoelke/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/nengo/nengo/labels/bug","name":"bug","color":"000000"}],"state":"closed","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2016-01-27T10:21:21Z","updated_at":"2016-01-27T11:25:35Z","closed_at":"2016-01-27T11:25:35Z","body":"When simulating higher order synapses, the `General` step method is invoked, which seems to be off-by-one delay, possibly due to the ordering in which `y` depends on `output`. \r\n\r\nThe following test shows that `scipy.signal.lfilter` agrees with the synapse implementation in the `Simple` step for a first-order synapse such as `Lowpass`, but not for a higher order differentiator.\r\n\r\n```\r\nimport pylab\r\nimport numpy as np\r\nfrom scipy.signal import lfilter, cont2discrete, tf2ss\r\n\r\nimport nengo\r\n\r\nsys1 = nengo.Lowpass(0.001)\r\nsys2 = nengo.LinearFilter([0.0001, 0, 0], [0.0001, 0.02, 1])\r\n\r\nfor sys in (sys1, sys2):\r\n    \r\n    with nengo.Network() as model:\r\n        x = nengo.Node(output=np.cos)\r\n        p = nengo.Probe(x, synapse=sys)\r\n        \r\n    sim = nengo.Simulator(model)\r\n    sim.run(0.02)\r\n    \r\n    (num,), den, _ = cont2discrete((sys.num, sys.den), sim.dt)\r\n    expected = lfilter(num, den, x.output(sim.trange()))\r\n\r\n    pylab.figure()\r\n    pylab.title(\"len(num)=%d, len(den)=%d\" % (len(sys.num), len(sys.den)))\r\n    pylab.plot(sim.trange(), sim.data[p])\r\n    pylab.plot(sim.trange(), expected)\r\n    pylab.show()\r\n```\r\n\r\n![download 15](https://cloud.githubusercontent.com/assets/5420057/12611683/12fec926-c4bc-11e5-80e1-4ec1a6d825ac.png)\r\n![download 16](https://cloud.githubusercontent.com/assets/5420057/12611684/1304e720-c4bc-11e5-9509-7ff0a9d0bfee.png)\r\n\r\nCurrently, [my implementation in NengoLib](https://github.com/arvoelke/nengolib/blob/fcbf86c80142e9e83127f949de9f06939f9a6354/nengolib/signal/system.py#L167) avoids this problem by simulating the state-space directly. Moreover, by putting it in controllable form, the `A` matrix reduces to a dot product and a shift operation. This requires less state than the LCCD method, and also reduces total simulation time."},"comment":{"url":"https://api.github.com/repos/nengo/nengo/issues/comments/175569366","html_url":"https://github.com/nengo/nengo/issues/938#issuecomment-175569366","issue_url":"https://api.github.com/repos/nengo/nengo/issues/938","id":175569366,"user":{"login":"arvoelke","id":5420057,"avatar_url":"https://avatars.githubusercontent.com/u/5420057?v=3","gravatar_id":"","url":"https://api.github.com/users/arvoelke","html_url":"https://github.com/arvoelke","followers_url":"https://api.github.com/users/arvoelke/followers","following_url":"https://api.github.com/users/arvoelke/following{/other_user}","gists_url":"https://api.github.com/users/arvoelke/gists{/gist_id}","starred_url":"https://api.github.com/users/arvoelke/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/arvoelke/subscriptions","organizations_url":"https://api.github.com/users/arvoelke/orgs","repos_url":"https://api.github.com/users/arvoelke/repos","events_url":"https://api.github.com/users/arvoelke/events{/privacy}","received_events_url":"https://api.github.com/users/arvoelke/received_events","type":"User","site_admin":false},"created_at":"2016-01-27T11:25:35Z","updated_at":"2016-01-27T11:25:35Z","body":"Actually I think there's something slightly more serious/general going on, with how the discretization has been incorrectly formulated to be preemptive with the state at the cost of effectively putting a delay on the passthrough. This basically means that transfer functions that are not strictly proper are not going to be able to behave correctly. However, I need to be more sure of what this means should happen in the more general setting, so I'm closing this issue for now. Sorry for the spam."}}