Finished the implementation of __pow__ for DDFunctions.
authorAntonio Jimenez Pastor <antonio@ebook.dk-compmath.jku.at>
Tue, 2 Oct 2018 11:23:30 +0000 (13:23 +0200)
committerAntonio Jimenez Pastor <antonio@ebook.dk-compmath.jku.at>
Tue, 2 Oct 2018 11:23:30 +0000 (13:23 +0200)
Now it allows to compute the power of DDFunctions to any other
DDFunction (if some conditions with the initial values hold).
See documentation of __pow__ in DDFunction class for more information.

ajpastor/dd_functions/ddFunction.py
releases/diff_defined_functions__0.6.zip
releases/old/diff_defined_functions__0.6__18.10.02_13:23:30.zip [new file with mode: 0644]

index 340f954..bc66494 100644 (file)
@@ -1958,13 +1958,32 @@ class DDFunction (IntegralDomainElement):
   
     # Integer powering
     def __pow__(self, other):
-        try:
-            return self.__pows[other];
-        except KeyError:
-            f = self;
-            if(f.is_null): ## Trivial case when order is 0
+        '''
+            Method to compute the power of a DDFunction to other object. The current implementation allow the user to compute the power to:
+                - Integers
+                - Rational numbers (to be done)
+                - Elements in self.parent().base_field (to be done)
+                - Other DDFunctions (with some conditions)
+                
+            The method works as follows:
+                1 - Tries to compute integer or rational power.
+                2 - If 'other' is neither in ZZ nor QQ, then try to cast 'other' to a DDFunction.
+                3 - Check if self(0) != 0. If self(0) != 1, check log(self(0)) in self.parent().base_field and set f2 = self/self(0).
+                4 - If other(0) != 0, compute g2 = other - other(0) and return self**other(0) * self**g2
+                5 - If other(0) == 0, compute ((log(self(0)) + log(f2))other)', and return the function with initial value 1.
+                
+                
+            The only case that is not included is when self(0) = 0 or self**other(0) is not implemented.
+        '''
+        if(other not in self.__pows):
+            f = self; g = other; f0 = f(x=0);
+            if(f.is_null or other == 0):
+                raise ValueError("Value 0**0 not well defined");
+            if(f.is_null): # Trivial case when f == 0, then f**g = 0.
                 self.__pows[other] = f;
-            elif(other in ZZ): ## Trying integer power
+            elif(g == 0): # Second trivial case when f != 0 and g == 0. Then f**g = 1
+                self.__pows[other] = f.parent().one();
+            elif(other in ZZ): # Integer case: can always be computed
                 other = int(other);
                 if(other >= 0 ):
                     a = other >> 1 ;
@@ -1981,10 +2000,31 @@ class DDFunction (IntegralDomainElement):
                     except Exception:
                         raise ZeroDivisionError("Impossible to compute the inverse");
                     return inverse.__pow__(-other);
-            else: ## Trying a generic power
-                if(is_DDFunction(other) or other in self.parent()):
-                    if(self(x=0) != 1):
-                        raise ValueError("The base of exponentiation must have initial value 1");
+            elif(g in f.parent().base_field): # Constant case: need extra condition (f(0) != 0 and f(0)**g is a valid element
+                g = f.parent().base_field(g);
+                if(f0 == 0):
+                    raise NotImplementedError("The powers for elements with f(0) == 0 is not implemented");
+                try:
+                    h0 = f0**g;
+                    if(not h0 in f.parent().base_field):
+                        raise ValueError("The initial value is not in the base field");
+                except Exception as e:
+                    raise ValueError("The power %s^%s could not be computed, hence the initial values can not be properly computed.\n\tReason: %s" %(f0,g,e));
+                R = f.parent().to_depth(f.parent().depth()+1);
+                name = None;
+                if(f.has_name()):
+                    name = DinamicString("(_1)^(%s)" %g, [repr(f)]);
+                self.__pows[other] = R.element([-other*f.derivative(), f], [h0]);
+            else: # Generic case: need extra condition (f(0) != 0, log(f(0)) and f(0)**g(0) are valid elements)
+                if(f0 == 0):
+                    raise NotImplementedError("The powers for elements with f(0) == 0 is not implemented");
+                lf0 = log(f0);
+                if(log(f0) not in f.parent().base_field):
+                    raise ValueError("Invalid value for log(f(0)). Need to be an element of %s, but got %s" %(f.parent().base_field, log(f0)));
+                lf0 = f.parent().base_field(lf0);
+                f = f/f0;
+                
+                if(is_DDFunction(g) or g in self.parent()):
                     from ajpastor.dd_functions.ddExamples import Log;
                     lf = Log(self);
                     
@@ -1992,28 +2032,30 @@ class DDFunction (IntegralDomainElement):
                     g = R(other); lf = R(lf);
                     R = R.to_depth(R.depth()+1);
                     
-                    if((g(x=0) != 0)):                    
-                        raise ValueError("The exponent must have initial value 0");
-                    
-                    newName = None;
-                    if((not is_DDFunction(g)) or (g._DDFunction__name is not None)):
-                        newName = DinamicString("(_1)^(_2)", [self.__name, repr(other)]);
-                    
-                    self.__pows[other] = R.element([-(lf*other).derivative(),1],[1],name=newName);
-                else:
-                    try:
-                        newDDRing = DDRing(self.parent());
-                        other = self.parent().base_ring()(other);
-                        self.__pows[other] = newDDRing.element([(-other)*f.derivative(),f], [el**other for el in f.getInitialValueList(1 )], check_init=False);
+                    g0 = g(x=0);
+                    if(g0 != 0):
+                        self.__pows[other] = self**g0 * self**(g-g0);
+                    else:
+                        name = None;
+                        if(g.has_name() and f.has_name()):
+                            nname = DinamicString("(_1)^(_2)", [f.__name, g.__name]);
                         
-                        newName = None;
-                        if(not(self.__name is None)):
-                            newName = DinamicString("(_1)^%s" %(other), self.__name);
-                        self.__pows[other].__name = newName;
-                    except TypeError:
-                        raise TypeError("Impossible to compute (%s)^(%s) within the basic field %s" %(f.getInitialValue(0 ), other, f.parent().base_ring()));
-                    except ValueError:
-                        raise NotImplementedError("Powering to an element of %s not implemented" %(other.parent()));
+                        self.__pows[other] = R.element([-((lf + lf0)*g).derivative(),1],[1],name=name);
+                else:
+                    raise NotImplementedError("No path found for this __pow__ computation:\n\t- base: %s\n\t- expo: %s" %(repr(self),repr(other)));
+                #    try:
+                #        newDDRing = DDRing(self.parent());
+                #        other = self.parent().base_ring()(other);
+                #        self.__pows[other] = newDDRing.element([(-other)*f.derivative(),f], [el**other for el in f.getInitialValueList(1 )], check_init=False);
+                #        
+                #        newName = None;
+                #        if(not(self.__name is None)):
+                #            newName = DinamicString("(_1)^%s" %(other), self.__name);
+                #        self.__pows[other].__name = newName;
+                #    except TypeError:
+                #        raise TypeError("Impossible to compute (%s)^(%s) within the basic field %s" %(f.getInitialValue(0 ), other, f.parent().base_ring()));
+                #    except ValueError:
+                #        raise NotImplementedError("Powering to an element of %s not implemented" %(other.parent()));
         return self.__pows[other];
         
             
index c4cca1b..e156066 100644 (file)
Binary files a/releases/diff_defined_functions__0.6.zip and b/releases/diff_defined_functions__0.6.zip differ
diff --git a/releases/old/diff_defined_functions__0.6__18.10.02_13:23:30.zip b/releases/old/diff_defined_functions__0.6__18.10.02_13:23:30.zip
new file mode 100644 (file)
index 0000000..e156066
Binary files /dev/null and b/releases/old/diff_defined_functions__0.6__18.10.02_13:23:30.zip differ