trueagi-io / metta-examples

Discussion of MeTTa programming with examples
MIT License
14 stars 15 forks source link

Could (if) return True instead of its content? #38

Closed DaddyWesker closed 4 months ago

DaddyWesker commented 4 months ago

So, probably it is a messy issue name, but I'll try to explain.

I'm currently working on symbolic differentiation example from SICP book. It is restricted currently to several possible input functions (type of functions). Every consequent exercise asks to extend possible function types which function could differentiate. Anyway, here is the current state of deriv function with other functions needed to launch it:

(= (null? $expr)
    (== $expr ()))

(= (cons $x $y)
    (cons-atom $x $y))

(= (car $x)
    (car-atom $x))

(= (cdr $x)
    (cdr-atom $x))

(= (cddr $x)
    (cdr (cdr $x)))

(= (cadr $x)
    (car (cdr $x)))

(= (caddr $x)
    (car (cdr (cdr $x))))

(= (number? $x) (and (== (get-type $x) Number) (not (== (get-metatype $x) Expression))))

(= (variable? $x) (== (get-metatype $x) Symbol))

(= (same-variable? $v1 $v2)
    (and$ ((variable? $v1) (variable? $v2) (== $v1 $v2))))

(= (sum? $x)
    (and (== (get-metatype $x) Expression) (== (car $x) '+)))

(= (addend ('+ $x $y)) $x)

(= (augend ('+ $x $y)) $y)

(= (product? $x)
    (and (== (get-metatype $x) Expression) (== (car $x) '*)))

(= (multiplier ('* $x $y)) $x)

(= (multiplicand ('* $x $y)) $y)

(= (and$ $list)
    (if (null? $list)
        True
        (and (car $list) (and$ (cdr $list)))))

(= (make-sum $a1 $a2)
  (if (=number? $a1 0)
        $a2
        (if (=number? $a2 0)
            $a1
            (if (and (number? $a1) (number? $a2))
                (+ $a1 $a2)
                ('+ $a1 $a2)))))

(= (=number? $exp $num)
    (and (number? $exp) (== $exp $num)))

(= (make-product $m1 $m2)
  (if (or (=number? $m1 0) (=number? $m2 0))
        0
        (if (=number? $m1 1)
            $m2
            (if (=number? $m2 1)
                $m1
                (if (and (number? $m1) (number? $m2))
                    (* $m1 $m2)
                    ('* $m1 $m2))))))

(= (deriv $exp $var)
      (if (number? $exp)
            0
            (if (variable? $exp)
                (if (same-variable? $exp $var) 1 0)
                (if (sum? $exp)
                     (make-sum (deriv (addend $exp) $var)
                               (deriv (augend $exp) $var))
                     (if (product? $exp)
                         (make-sum
                              (make-product (multiplier $exp)
                                            (deriv (multiplicand $exp) $var))
                              (make-product (deriv (multiplier $exp) $var)
                                            (multiplicand $exp)))
                         (if (exponentiation? $exp)
                            (make-exponentiation (base $exp) (deriv (base $exp) $var) (exponent $exp))
                            (Error (deriv $exp $var) "Unknown expression type")))))))

(= (exp) ('+ x ('+ x 1)))

!(assertEqual
    (deriv (exp) x)
    2)

Okay, here everything works. But next exercise asks to extend functions added, augend, multiplier and multiplicand so we can evaluate deriv for sums/products of several (more than 2) terms. In this case, I've introduced following changes:

(= (length $items)
    (if (null? $items)
        0
        (+ 1 (length (cdr $items)))))

(= (addend $x) (cadr $x))

(= (augend $x)
    (if (> (length $x) 3)
        (cons '+ (cddr $x))
        (caddr $x)))

(= (multiplier $x) (cadr $x))

(= (multiplicand $x)
    (if (> (length $x) 3)
        (cons '* (cddr $x))
        (cddr $x)))

And here deriv becomes unstable (of course I've deleted previous augend/addend/multiplier/multiplicand). Lets take same example:

!(deriv (exp) x)

(if (and (number? (if (=number? (Error (deriv True x) "Unknown expression type") 0) 1 (if (and (number? 1) (number? (Error (deriv True x) "Unknown expression type"))) (+ 1 (Error (deriv True x) "Unknown expression type")) ('+ 1 (Error (deriv True x) "Unknown expression type"))))) False) 1 (if (and (number? 1) (number? (if (=number? (Error (deriv True x) "Unknown expression type") 0) 1 (if (and (number? 1) (number? (Error (deriv True x) "Unknown expression type"))) (+ 1 (Error (deriv True x) "Unknown expression type")) ('+ 1 (Error (deriv True x) "Unknown expression type")))))) (+ 1 (if (=number? (Error (deriv True x) "Unknown expression type") 0) 1 (if (and (number? 1) (number? (Error (deriv True x) "Unknown expression type"))) (+ 1 (Error (deriv True x) "Unknown expression type")) ('+ 1 (Error (deriv True x) "Unknown expression type"))))) ('+ 1 (if (=number? (Error (deriv True x) "Unknown expression type") 0) 1 (if (and (number? 1) (number? (Error (deriv True x) "Unknown expression type"))) (+ 1 (Error (deriv True x) "Unknown expression type")) ('+ 1 (Error (deriv True x) "Unknown expression type")))))))

So for the same input deriv now outputs garbage. Moreover, for some reason, $exp somehow becomes True as we can see from (Error (deriv True x) "Unknown expression type") . I don't know how is this possible. If we will use previous augend/multiplicand this problem will disappear (but of course previous functions don't know about possible several terms in sums and products). And I don't get where this True comes from? Since there is no debug in Metta, I've tried to go step-by-step through deriv just by calling consequent lines outside the function and wasn't able to get this behavior anywhere. Here is an interesting thing:

!(augend (exp)) gives ('+ x 1)

which is correct. !(deriv (augend (exp)) x) gives 1 which is correct. But it doesn't work inside function deriv for some reason. I've spent many hours to "debug" this problem and wasn't able to do this. I don't get where is True coming from. The only possible way of getting it inside $exp variable is that augend with if added returns "True" instead of evaluation of its first line. Is it even possible? And why it's not happening when I'm calling augend outside the deriv function? I'm beaten here so I'll be really glad to hear possible solutions. I've thought about introducing type for augend, but since its input and output are %Undefined% it makes no difference. I need to tell this function that it could output anything BUT Bool, but how? And will it work?

@vsbogd @Necr0x0Der

DaddyWesker commented 4 months ago

Well, it seems that problem not in if statement. If I replace augend with: (= (augend $x) (caddr $x)) I'm still getting the same error. I don't get it. I wish there was some sort of step-by-step debugger.

vsbogd commented 4 months ago

This behaviour is caused by https://github.com/trueagi-io/hyperon-experimental/issues/601 When code is changed as @DaddyWesker describes at some point interpreter evaluates the result of (True) and caches it as (True) -> (True). After a while it evaluates (cdr (1)), (1) is an expression and it is evaluated first. Interpreter searches (1) in cache and finds (True), finally it returns (True) and evaluates (cdr (True)) which is True.

vsbogd commented 4 months ago

One way to eliminate this is to use minimal MeTTa which doesn't use caching.

vsbogd commented 4 months ago

Another manner is to run REPL without Python support: cargo run --features no_python --bin metta One can also install it: cargo install --features no_python install ./repl

DaddyWesker commented 4 months ago

Okay, I can confirm, that this code works with minimal metta. Thanks.