@@ -300,135 +300,132 @@ function analyze_expr(f::SymbolicUtils.BasicSymbolic{SymbolicUtils.SymReal} , fu
300300 return f
301301 end
302302
303- # For non-symbols, dispatch based on operation
304- try
305- op = operation (f)
306- if op in (+ , * , / )
307- # Handle Add, Mul, Div operations
308- as = [SymbolicUtils. unwrap_const (x) for x in arguments (f)]
309- ps = [analyze_expr (a, funs, vars, args, tanArgs, expArgs) for a in as]
310- return op (ps... )
311- elseif op == (^ )
312- # Handle Pow operations
313- as = [SymbolicUtils. unwrap_const (x) for x in arguments (f)]
314- p1 = analyze_expr (as[1 ], funs, vars, args, tanArgs, expArgs)
315- p2 = analyze_expr (as[2 ], funs, vars, args, tanArgs, expArgs)
316- if isa (p2, Integer)
317- return p1^ p2
318- elseif isa (p2, Number)
319- throw (NotImplementedError (" integrand contains power with unsupported exponent $p2 " ))
320- end
303+ # For non-symbols, dispatch based on operation. No catch-all here on
304+ # purpose: domain limits are raised explicitly as `NotImplementedError`
305+ # at the right point; everything else (MethodError/TypeError/...) is a
306+ # bug and should surface as itself rather than be re-wrapped as
307+ # "unsupported expression", which makes upstream API breakage look like
308+ # a coverage gap.
309+ op = operation (f)
310+ if op in (+ , * , / )
311+ # Handle Add, Mul, Div operations
312+ as = [SymbolicUtils. unwrap_const (x) for x in arguments (f)]
313+ ps = [analyze_expr (a, funs, vars, args, tanArgs, expArgs) for a in as]
314+ return op (ps... )
315+ elseif op == (^ )
316+ # Handle Pow operations
317+ as = [SymbolicUtils. unwrap_const (x) for x in arguments (f)]
318+ p1 = analyze_expr (as[1 ], funs, vars, args, tanArgs, expArgs)
319+ p2 = analyze_expr (as[2 ], funs, vars, args, tanArgs, expArgs)
320+ if isa (p2, Integer)
321+ return p1^ p2
322+ elseif isa (p2, Number)
321323 throw (NotImplementedError (" integrand contains power with unsupported exponent $p2 " ))
322- elseif op == exp
323- # Handle exp function
324- a = arguments (f)[1 ]
325- i = findfirst (x -> is_rational_multiple (a, x), expArgs)
326- n = 1
327- if i === nothing
328- push! (expArgs, a)
329- else
330- n = rational_multiple (a, expArgs[i])
331- if ! isone (denominator (n)) # n not an integer
332- expArgs[i] = 1 // denominator (n)* expArgs[i]
333- throw (UpdatedArgList ())
334- end
335- n = numerator (n)
336- end
337- if n != 1
338- f_new = exp (expArgs[i])^ n
339- return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
340- end
341- # Continue to general function handling below
342- elseif op == tan
343- # Handle tan function
344- a = arguments (f)[1 ]
345- i = findfirst (x -> is_rational_multiple (a, x), tanArgs)
346- n = 1
347- if i === nothing
348- push! (tanArgs, a)
349- else
350- n = rational_multiple (a, tanArgs[i])
351- if ! isone (denominator (n)) # n not an integer
352- tanArgs[i] = 1 // denominator (n)* tanArgs[i]
353- throw (UpdatedArgList ())
354- end
355- n = numerator (n)
356- end
357- if n != 1
358- f_new = tan_nx (n, tanArgs[i])
359- return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
324+ end
325+ throw (NotImplementedError (" integrand contains power with unsupported exponent $p2 " ))
326+ elseif op == exp
327+ # Handle exp function
328+ a = arguments (f)[1 ]
329+ i = findfirst (x -> is_rational_multiple (a, x), expArgs)
330+ n = 1
331+ if i === nothing
332+ push! (expArgs, a)
333+ else
334+ n = rational_multiple (a, expArgs[i])
335+ if ! isone (denominator (n)) # n not an integer
336+ expArgs[i] = 1 // denominator (n)* expArgs[i]
337+ throw (UpdatedArgList ())
360338 end
361- # Continue to general function handling below
362- elseif op == sinh
363- # Transform sinh to exponentials
364- a = arguments (f)[1 ]
365- f_new = 1 // 2 * (exp (a) - 1 / exp (a))
366- return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
367- elseif op == cosh
368- # Transform cosh to exponentials
369- a = arguments (f)[1 ]
370- f_new = 1 // 2 * (exp (a) + 1 / exp (a))
371- return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
372- elseif op == csch # 1/sinh
373- a = arguments (f)[1 ]
374- f_new = 2 / (exp (a) - 1 / exp (a))
375- return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
376- elseif op == sech
377- a = arguments (f)[1 ]
378- f_new = 2 / (exp (a) + 1 / exp (a))
379- return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
380- elseif op == tanh
381- a = arguments (f)[1 ]
382- f_new = (exp (a) - 1 / exp (a))/ (exp (a) + 1 / exp (a))
383- return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
384- elseif op == coth
385- a = arguments (f)[1 ]
386- f_new = (exp (a) + 1 / exp (a))/ (exp (a) - 1 / exp (a))
387- return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
388- elseif op == sin # transform to half angle format
389- a = arguments (f)[1 ]
390- f_new = 2 * tan (1 // 2 * a)/ (1 + tan (1 // 2 * a)^ 2 )
391- return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
392- elseif op == cos
393- a = arguments (f)[1 ]
394- f_new = (1 - tan (1 // 2 * a)^ 2 )/ (1 + tan (1 // 2 * a)^ 2 )
395- return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
396- elseif op == csc # 1/sin
397- a = arguments (f)[1 ]
398- f_new = 1 // 2 * (1 + tan (1 // 2 * a)^ 2 )/ tan (1 // 2 * a)
399- return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
400- elseif op == sec # 1/cos
401- a = arguments (f)[1 ]
402- f_new = (1 + tan (1 // 2 * a)^ 2 )/ (1 - tan (1 // 2 * a)^ 2 )
403- return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
404- elseif op == cot
405- a = arguments (f)[1 ]
406- f_new = 1 / tan (a)
339+ n = numerator (n)
340+ end
341+ if n != 1
342+ f_new = exp (expArgs[i])^ n
407343 return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
408344 end
409-
410- # General function handling (for exp, log, atan, tan that didn't get transformed above)
411- i = findfirst (x -> hash (x)== hash (f), funs)
412- if i != = nothing
413- return vars[i]
414- end
415- op in [exp, log, atan, tan] ||
416- throw (NotImplementedError (" integrand contains unsupported function $op " ))
345+ # Continue to general function handling below
346+ elseif op == tan
347+ # Handle tan function
417348 a = arguments (f)[1 ]
418- p = analyze_expr (a, funs, vars, args, tanArgs, expArgs)
419- tname = Symbol (:t , length (vars))
420- t = SymbolicUtils. Sym {Real} (tname)
421- push! (funs, f)
422- push! (vars, t)
423- push! (args, p)
424- return t
425- catch e
426- if isa (e, UpdatedArgList)
427- rethrow (e)
349+ i = findfirst (x -> is_rational_multiple (a, x), tanArgs)
350+ n = 1
351+ if i === nothing
352+ push! (tanArgs, a)
428353 else
429- throw (NotImplementedError (" integrand contains unsupported expression $f " ))
354+ n = rational_multiple (a, tanArgs[i])
355+ if ! isone (denominator (n)) # n not an integer
356+ tanArgs[i] = 1 // denominator (n)* tanArgs[i]
357+ throw (UpdatedArgList ())
358+ end
359+ n = numerator (n)
430360 end
361+ if n != 1
362+ f_new = tan_nx (n, tanArgs[i])
363+ return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
364+ end
365+ # Continue to general function handling below
366+ elseif op == sinh
367+ # Transform sinh to exponentials
368+ a = arguments (f)[1 ]
369+ f_new = 1 // 2 * (exp (a) - 1 / exp (a))
370+ return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
371+ elseif op == cosh
372+ # Transform cosh to exponentials
373+ a = arguments (f)[1 ]
374+ f_new = 1 // 2 * (exp (a) + 1 / exp (a))
375+ return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
376+ elseif op == csch # 1/sinh
377+ a = arguments (f)[1 ]
378+ f_new = 2 / (exp (a) - 1 / exp (a))
379+ return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
380+ elseif op == sech
381+ a = arguments (f)[1 ]
382+ f_new = 2 / (exp (a) + 1 / exp (a))
383+ return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
384+ elseif op == tanh
385+ a = arguments (f)[1 ]
386+ f_new = (exp (a) - 1 / exp (a))/ (exp (a) + 1 / exp (a))
387+ return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
388+ elseif op == coth
389+ a = arguments (f)[1 ]
390+ f_new = (exp (a) + 1 / exp (a))/ (exp (a) - 1 / exp (a))
391+ return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
392+ elseif op == sin # transform to half angle format
393+ a = arguments (f)[1 ]
394+ f_new = 2 * tan (1 // 2 * a)/ (1 + tan (1 // 2 * a)^ 2 )
395+ return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
396+ elseif op == cos
397+ a = arguments (f)[1 ]
398+ f_new = (1 - tan (1 // 2 * a)^ 2 )/ (1 + tan (1 // 2 * a)^ 2 )
399+ return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
400+ elseif op == csc # 1/sin
401+ a = arguments (f)[1 ]
402+ f_new = 1 // 2 * (1 + tan (1 // 2 * a)^ 2 )/ tan (1 // 2 * a)
403+ return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
404+ elseif op == sec # 1/cos
405+ a = arguments (f)[1 ]
406+ f_new = (1 + tan (1 // 2 * a)^ 2 )/ (1 - tan (1 // 2 * a)^ 2 )
407+ return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
408+ elseif op == cot
409+ a = arguments (f)[1 ]
410+ f_new = 1 / tan (a)
411+ return analyze_expr (f_new, funs, vars, args, tanArgs, expArgs)
431412 end
413+
414+ # General function handling (for exp, log, atan, tan that didn't get transformed above)
415+ i = findfirst (x -> hash (x)== hash (f), funs)
416+ if i != = nothing
417+ return vars[i]
418+ end
419+ op in [exp, log, atan, tan] ||
420+ throw (NotImplementedError (" integrand contains unsupported function $op " ))
421+ a = arguments (f)[1 ]
422+ p = analyze_expr (a, funs, vars, args, tanArgs, expArgs)
423+ tname = Symbol (:t , length (vars))
424+ t = SymbolicUtils. Sym {SymbolicUtils.SymReal} (tname; type= Real)
425+ push! (funs, f)
426+ push! (vars, t)
427+ push! (args, p)
428+ return t
432429end
433430
434431function analyze_expr (f:: Number , funs:: Vector , vars:: Vector{SymbolicUtils.BasicSymbolic{SymbolicUtils.SymReal}} , args:: Vector , tanArgs:: Vector , expArgs:: Vector )
0 commit comments