Example #1
0
class DifferentialPolynomialRing:
    element_class = DifferentialPolynomial

    def __init__(self, base_ring, fibre_names, base_names,
                 max_differential_orders):
        self._fibre_names = tuple(fibre_names)
        self._base_names = tuple(base_names)
        self._max_differential_orders = tuple(max_differential_orders)
        base_dim = len(self._base_names)
        fibre_dim = len(self._fibre_names)
        jet_names = []
        idx_to_name = {}
        for fibre_idx in range(fibre_dim):
            u = self._fibre_names[fibre_idx]
            idx_to_name[(fibre_idx, ) + tuple([0] * base_dim)] = u
            for d in range(1, max_differential_orders[fibre_idx] + 1):
                for multi_index in IntegerVectors(d, base_dim):
                    v = '{}_{}'.format(
                        u, ''.join(self._base_names[i] * multi_index[i]
                                   for i in range(base_dim)))
                    jet_names.append(v)
                    idx_to_name[(fibre_idx, ) + tuple(multi_index)] = v
        self._polynomial_ring = PolynomialRing(
            base_ring, base_names + fibre_names + tuple(jet_names))
        self._idx_to_var = {
            idx: self._polynomial_ring(idx_to_name[idx])
            for idx in idx_to_name
        }
        self._var_to_idx = {
            jet: idx
            for (idx, jet) in self._idx_to_var.items()
        }
        # for conversion:
        base_vars = [var(b) for b in self._base_names]
        symbolic_functions = [
            function(f)(*base_vars) for f in self._fibre_names
        ]
        self._subs_jet_vars = SubstituteJetVariables(symbolic_functions)
        self._subs_tot_ders = SubstituteTotalDerivatives(symbolic_functions)

    def __repr__(self):
        return 'Differential Polynomial Ring in {} over {}'.format(
            ', '.join(map(repr, self._polynomial_ring.gens())),
            self._polynomial_ring.base_ring())

    def _latex_(self):
        return self._polynomial_ring._latex_()

    def base_ring(self):
        return self._polynomial_ring.base_ring()

    def _first_ngens(self, n):
        return tuple(
            self.element_class(self, self._polynomial_ring.gen(i))
            for i in range(n))

    def gens(self):
        return self._first_ngens(self._polynomial_ring.ngens())

    def gen(self, i):
        return self.element_class(self, self._polynomial_ring.gen(i))

    def base_variables(self):
        return self._first_ngens(len(self._base_names))

    def base_dim(self):
        return len(self._base_names)

    def fibre_variable(self, i):
        return self.element_class(
            self, self._polynomial_ring.gen(len(self._base_names) + i))

    def fibre_variables(self):
        base_dim = len(self._base_names)
        fibre_dim = len(self._fibre_names)
        return tuple(
            self.element_class(self, self._polynomial_ring.gen(base_dim + i))
            for i in range(fibre_dim))

    def fibre_dim(self):
        return len(self._fibre_names)

    def jet_variables(self):
        base_dim = len(self._base_names)
        fibre_dim = len(self._fibre_names)
        whole_dim = self._polynomial_ring.ngens()
        return tuple(
            self.element_class(self, self._polynomial_ring.gen(i))
            for i in range(base_dim + fibre_dim, whole_dim))

    def max_differential_orders(self):
        return self._max_differential_orders

    def _single_var_weights(self, u):
        return self._var_to_idx[u][1:]

    def _diff_single_var(self, u, x):
        x_idx = self._polynomial_ring.gens().index(x)
        u_idx = self._var_to_idx[u]
        du_idx = list(u_idx)
        du_idx[1 + x_idx] += 1
        du_idx = tuple(du_idx)
        if du_idx in self._idx_to_var:
            return self._idx_to_var[du_idx]
        else:
            raise ValueError(
                "can't differentiate {} any further with respect to {}".format(
                    u, x))

    def _integrate_single_var(self, u, x):
        x_idx = self._polynomial_ring.gens().index(x)
        u_idx = self._var_to_idx[u]
        if u_idx[1 + x_idx] == 0:
            raise ValueError(
                "can't integrate {} any further with respect to {}".format(
                    u, x))
        iu_idx = list(u_idx)
        iu_idx[1 + x_idx] -= 1
        iu_idx = tuple(iu_idx)
        return self._idx_to_var[iu_idx]

    def __contains__(self, arg):
        if isinstance(arg, self.element_class) and arg.parent() is self:
            return True
        if arg in self._polynomial_ring.base_ring():
            return True
        return False

    def __call__(self, arg):
        if isinstance(arg, self.element_class) and arg.parent() is self:
            return arg
        if is_Expression(arg):
            arg = self._subs_jet_vars(arg)
        return self.element_class(self, self._polynomial_ring(arg))

    def zero(self):
        return self.element_class(self, self._polynomial_ring.zero())

    def one(self):
        return self.element_class(self, self._polynomial_ring.one())

    def homogeneous_monomials(self,
                              fibre_degrees,
                              weights,
                              max_differential_orders=None):
        fibre_vars = self.fibre_variables()
        if not len(fibre_degrees) == len(fibre_vars):
            raise ValueError(
                'length of fibre_degrees vector must match number of fibre variables'
            )
        base_vars = self.base_variables()
        if not len(weights) == len(base_vars):
            raise ValueError(
                'length of weights vector must match number of base variables')
        monomials = []
        fibre_degree = sum(fibre_degrees)
        fibre_indexes = {}
        fibre_idx = 0
        for i in range(len(fibre_degrees)):
            for j in range(fibre_degrees[i]):
                fibre_indexes[fibre_idx] = i
                fibre_idx += 1
        proto = sum([[fibre_vars[i]] * fibre_degrees[i]
                     for i in range(len(fibre_degrees))], [])
        for V in product(*[IntegerVectors(w, fibre_degree) for w in weights]):
            total_differential_order = [0 for i in range(fibre_degree)]
            term = [p for p in proto]
            skip = False
            for j in range(fibre_degree):
                fibre_idx = fibre_indexes[j]
                for i in range(len(base_vars)):
                    if V[i][j] > 0:
                        total_differential_order[j] += V[i][j]
                        if max_differential_orders is not None and total_differential_order[
                                j] > max_differential_orders[fibre_idx]:
                            skip = True
                            break
                        term[j] = term[j].total_derivative(*([base_vars[i]] *
                                                             V[i][j]))
                if skip:
                    break
            if not skip:
                monomials.append(prod(term))
        return monomials