Menu

#4748 featurep(X,'real) misses an important case

None
open
nobody
None
5
2 hours ago
2 days ago
No

OK:

(%i7) featurep(x^2,real);
(%o7)                                true

Should be true

(%i8) featurep(x,real);
(%o8)                                false

Discussion

  • David Scherfgen

    David Scherfgen - 2 days ago

    Good catch!

    Since x is a symbol, featurep(x, real) is answered directly by kindp, which checks the database, but since nothing is stored on x, it says nil.

    featurep(x^2, real) calls $rectform and checks if it contains %i. Since it doesn't, the answer is nil. By the way, I don't like that. Why not check if $imagpart is 0 instead? The test suite seems to work fine with this, and it should be slightly faster.

    Maybe featurep(expr, real) should always use the imagpart(expr) = 0 check regardless of whether expr is a symbol or not?

     
  • David Scherfgen

    David Scherfgen - 2 days ago

    Similarly wrong:

    (%i1) featurep(%i, complex);
    (%o1) true
    
    (%i2) featurep(%i+1, complex);
    (%o2) false
    

    featurep doesn't have a special case for complex, unlike for real. featurep(%i, complex) is answered by kindp, but featurep(%i+1, complex) isn't handled by any test, and we get nil.

    Should featurep(expr, complex) test whether imagpart(expr) # 0? Of course # 0 doesn't mean that it's really non-zero. But that's better than nothing. I definitely wouldn't advocate for using ratsimp or even zeroequiv here.

     
  • David Scherfgen

    I suggest using the following cond clauses in $featurep:

            ((eq ind '$real)
              (if (symbolp e)
                (not (decl-complexp e))
                (zerop1 ($imagpart e))))
            ((eq ind '$complex)
              (if (symbolp e)
                (decl-complexp e)
                (not (zerop1 ($imagpart e)))))
            ((eq ind '$imaginary)
              (if (symbolp e)
                (kindp e '$imaginary)
                (destructuring-let (((rpart . ipart) (trisplit e)))
                  (and (zerop1 rpart) (not (zerop1 ipart))))))
    

    With these changes:

    (%i1) featurep(x, real);
    (%o1)                                true
    (%i2) featurep(x+1, real);
    (%o2)                                true
    (%i3) featurep(%i, imaginary);
    (%o3)                                true
    (%i4) featurep(%i+1, imaginary);
    (%o4)                                false
    (%i5) featurep(%i, complex);
    (%o5)                                true
    (%i6) featurep(%i+1, complex);
    (%o6)                                true
    (%i7) declare(j, imaginary);
    (%o7)                                done
    (%i8) featurep(j, real);
    (%o8)                                false
    (%i9) featurep(j^2, real);
    (%o9)                                true
    

    The test suite is also fine with these changes.
    Unless there are objections, I will make these changes within the next couple of days and add some test cases.

     

    Last edit: David Scherfgen 1 day ago
    • Stavros Macrakis

      Stavros Macrakis - 12 hours ago

      The code looks correct.

      I do have a problem with featurep, though: it has strayed very far from its original function, which was simply looking up properties in the database. This isn't new -- I think it started at MIT in the 70's -- but that changed it from a simple and clearly-defined lookup to a "best effort" inference function where you don't know how reliable the results are. Simple examples work fine:

      featurep(%pi,irrational) => true   << OK, lookup
      declare(eee,even)$ featurep(2*eee,even) => true << OK, inference
      

      But the definitional drift means that its return value is hard to interpret. A best-effort function to determine whether an object has a certain property should not return true/false meaning Yes/Unknown, but rather true/false/unknown. It's perfectly OK that featurep can't determine whether the following cases are true, but wouldn't it be nice if it said unknown instead of false? After all, it is impossible to handle all cases.

      featurep(%pi/2,irrational)
      featurep(sqrt(2),irrational)
      featurep(sin(1)^2+cos(1)^2,integer)
      
      declare(ii,integer)$ assume(ii>1)$ 
      featurep(ii!,even) => false
      
       
      • David Scherfgen

        David Scherfgen - 2 hours ago

        Yes, allowing featurep to return unknown would be a great addition.

        There's another (smaller) problem: The manual says that real, complex and imaginary can be assigned to variables via declare. However, this also works for (user-defined) functions:

        (%i1) declare(f, real, g, complex)$
        
        (%i2) rectform(f(x));
        (%o2) f(x);
        
        (%i3) rectform(g(x));
        (%o3) realpart(g(x)) + %i*imagpart(g(x))
        
        /* h(x) treated like complex by default */
        (%i4) rectform(h(x));
        (%o4) realpart(h(x)) + %i*imagpart(h(x))
        

        A comment in rpart.lisp says:

        ;;; A MAJOR ASSUMPTION:
        ;;;  All random functions are pure real, regardless of argument.
        

        Seems like that comment is outdated: Maxima treats h(x) like complex by default. The discrepancy: While variables are treated as real by default, functions apparently are treated as complex by default.

        The problem: When using declare, there's no way to distinguish between a variable and a function. A declaration always applies to both.

        For sign, there is a feature especially for functions: posfun. So if one wants to say that a function f(x) is always positive, one calls declare(f, posfun), while for a variable f, one would call assume(f > 0).

        Maybe add a feature realfun?

         

        Last edit: David Scherfgen 2 hours ago

Log in to post a comment.

MongoDB Logo MongoDB