You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
describing_function_response reports sin(w*dt), not w, for discrete-time systems, and refines at z = jw #1269
For a discrete-time system, describing_function_response finds the right amplitude but reports the wrong frequency. It returns about sin(ω·dt), the same number whatever dt is. Its refinement step, and the returned positions, evaluate H at z = jω rather than on the unit circle.
importwarningsimportnumpyasnpimportcontrolasctwarnings.simplefilter("ignore")
fordtin (1, 0.5):
H=ct.tf([0.5], [1, -0.5, 0], dt=dt)
# where the Nyquist curve, H(exp(j w dt)), crosses the negative real axisw=np.linspace(1e-4, np.pi/dt, 200_001)
Hw=H(np.exp(1j*w*dt))
k=np.flatnonzero((np.diff(np.sign(Hw.imag)) !=0) & (Hw.real[:-1] <0))[0]
r=ct.describing_function_response(H, np.sign, np.linspace(0.01, 5, 400), omega=np.linspace(1e-3, np.pi/dt, 2000))
a, om=r.intersections[0]
print(f"dt={dt}: crossing at w={w[k]:.4f}, H={Hw[k].real:.3f} -> a={4/np.pi*abs(Hw[k].real):.4f}; "f"describing_function_response: a={a:.4f}, omega={om:.4f}")
Output, with control 0.10.2 and the same on main (bfe9380):
dt=1: crossing at w=1.3181, H=-0.500 -> a=0.6366; describing_function_response: a=0.6339, omega=0.9703
dt=0.5: crossing at w=2.6362, H=-0.500 -> a=0.6366; describing_function_response: a=0.6339, omega=0.9703
With the default refine=True it also warns not able to refine result; returning estimate.
Cause (control/descfcn.py on main):
The frequency. Line 349 is H_omega, H_vals = response.contour.imag, H(response.contour). For a discrete system, nyquist_response's contour is in z, exp(jω·dt), so its imaginary part is about sin(ω·dt), not ω (sin(1.318) = 0.968). The intersection itself is found on H(response.contour), which is why the amplitude is right.
The refinement and the positions._cost (line 378) and pos (line 389) use H(1j * omega), which for a discrete system evaluates at z = jω.
Suggested fix: when H.isdtime(), take the frequencies as np.angle(response.contour) / H.dt, and evaluate H(np.exp(1j * omega * H.dt)) in _cost and for pos. A test on a discrete loop with a known crossing, such as the one above, would cover it.
Workaround: compute the crossing on the unit circle directly, as in the first lines of the example, and use describing_function for N(a).
Disclosure: yes, this was written with an AI agent, Claude Code (Anthropic's Claude Opus 5.5 model).
How it was used: the agent hit the problem while testing a describing-function analysis of a sampled loop for a research project of mine. It wrote the reproduction, ran it against control 0.10.2 and against main, wrote the issue text, and posted it through the GitHub CLI at my request.
What's AI-generated: all of the text and code in the issue. The numbers are program output from those runs, and the code block runs as posted.
My part: I asked for it to be filed, but didn't review the text before it was posted.
I'm responsible for what's posted under my account, and questions are welcome.
Anyway, you are welcome to use AI tools, but you absolutely must review stuff before you post it. (That said, I appreciate your honesty.)
Hi, sorry for the frustration. You're point is fair, it was a relatively low effort issue filing on my part. I'm prototyping analysis tools a bit ahead of my complete understanding with regard to control theory analysis and the algorithms underlying all that.
Having said that I was involved in checking that the analysis of the bug, I did an adversarial review with ponytail to simplify/minimize the issue content (which i find works pretty well) but i trusted the agent to largely copy/paste what i read from the adversarial review to github. So the 'without review' phrasing is referring to the final copy not the whole interaction. But to your point i'm adding in a rule to have the agent quiz me on key points as an automated guard rail to check comprehension before posting publicly.
@gimballock Thanks, that is a good plan. I appreciate that you are making your contributions sincerely and carefully.
As you know, the cost of submitting plausible issues and PRs is now almost zero, but the effort of review has not meaningfully become less. AI tools can help with review, but humans must sign-off, and maintainers of open source bear responsibility of the consequences of new/edited code. (Fire-and-forget AI users like RoboX2020 are thus very frustrating to work with.)
@gimballock I read over this again, and I think this generation process is unreliable but maybe is the best you can do. Ideally, to me, you would write the description yourself, assuming you understand the issue/PR. At the very least, you need to commit to review 100% of the generated issue, PR, comment, etc. text before posting it. Only having the chat bot "quiz" you is unacceptable. (If you expect me to read the post, then you should read it first, from beginning to end.)
Activity
slivingston commentedon Nov 5, 2026
@gimballock Please add a disclosure about tool use if appropriate: https://numpy.org/doc/stable/dev/ai_policy.html
gimballock commentedon Nov 5, 2026
Disclosure: yes, this was written with an AI agent, Claude Code (Anthropic's Claude Opus 5.5 model).
main, wrote the issue text, and posted it through the GitHub CLI at my request.I'm responsible for what's posted under my account, and questions are welcome.
slivingston commentedon Nov 5, 2026
@gimballock You didn't even review the text before posting it? So you just posted generated text without review and expect us to review it for you?
slivingston commentedon Nov 5, 2026
Anyway, you are welcome to use AI tools, but you absolutely must review stuff before you post it. (That said, I appreciate your honesty.)
gimballock commentedon Nov 5, 2026
Hi, sorry for the frustration. You're point is fair, it was a relatively low effort issue filing on my part. I'm prototyping analysis tools a bit ahead of my complete understanding with regard to control theory analysis and the algorithms underlying all that.
Having said that I was involved in checking that the analysis of the bug, I did an adversarial review with ponytail to simplify/minimize the issue content (which i find works pretty well) but i trusted the agent to largely copy/paste what i read from the adversarial review to github. So the 'without review' phrasing is referring to the final copy not the whole interaction. But to your point i'm adding in a rule to have the agent quiz me on key points as an automated guard rail to check comprehension before posting publicly.
slivingston commentedon Nov 5, 2026
@gimballock Thanks, that is a good plan. I appreciate that you are making your contributions sincerely and carefully.
As you know, the cost of submitting plausible issues and PRs is now almost zero, but the effort of review has not meaningfully become less. AI tools can help with review, but humans must sign-off, and maintainers of open source bear responsibility of the consequences of new/edited code. (Fire-and-forget AI users like RoboX2020 are thus very frustrating to work with.)
slivingston commentedon Nov 6, 2026
@gimballock I read over this again, and I think this generation process is unreliable but maybe is the best you can do. Ideally, to me, you would write the description yourself, assuming you understand the issue/PR. At the very least, you need to commit to review 100% of the generated issue, PR, comment, etc. text before posting it. Only having the chat bot "quiz" you is unacceptable. (If you expect me to read the post, then you should read it first, from beginning to end.)
slivingston commentedon Nov 6, 2026
By the way, read links under https://numpy.org/doc/stable/dev/ai_policy.html#other-resources if you do not understand why "low effort issue filing" is a problem for the projects you want to contribute to.