def gen_ode_system_convergence()

in src/envs/ode.py [0:0]


    def gen_ode_system_convergence(self, return_system=False):
        """
        Generate systems of functions, and the corresponding convergence speed in zero.
        Start by generating a random system S, use SymPy to compute formal jacobian
        and evaluate it in zero, find largest eigenvalue
        Encode this as a prefix sensence
        """
        degree = self.rng.randint(self.min_degree, self.max_degree + 1)
        nb_ops = self.rng.randint(
            self.min_expr_len_factor_cspeed * degree + 3,
            self.max_expr_len_factor_cspeed * degree + 3,
            size=(degree,),
        )

        while True:
            system = []
            i = 0
            ngen = 0
            while i < degree:
                # generate expression
                expr = self.generate_tree(nb_ops[i], degree)
                ngen += 1
                # sympy zone
                try:
                    expr_sp = sp.S(expr, locals=self.local_dict)
                    # skip constant or invalid expressions
                    if len(expr_sp.free_symbols) == 0 or has_inf_nan(expr_sp):
                        continue
                    # evaluate gradient in point
                    values = self.compute_gradient(expr_sp, self.eval_point, degree)
                    if np.isnan(values).any() or np.isinf(values).any():
                        continue
                    if self.skip_zero_gradient and not values.any():
                        continue
                except TimeoutError:
                    continue
                except (ValueError, TypeError):
                    continue
                except Exception as e:
                    logger.error(
                        "An unknown exception of type {0} occurred in line {1} "
                        'for expression "{2}". Arguments:{3!r}.'.format(
                            type(e).__name__,
                            sys.exc_info()[-1].tb_lineno,
                            expr_sp,
                            e.args,
                        )
                    )
                    continue

                system.append(expr)
                if i == 0:
                    jacobian = values
                else:
                    jacobian = np.vstack((jacobian, values))
                i += 1
            if self.skip_zero_gradient:
                skip = False
                for i in range(degree):
                    if not jacobian[:, [i]].any():
                        skip = True
                        break
                if skip:
                    continue

            cspeed = -max(np.linalg.eigvals(jacobian).real)

            if self.prob_positive == 0 and cspeed > 0:
                continue
            if self.prob_positive == 1 and cspeed <= 0:
                continue
            if (
                self.prob_positive > 0
                and self.prob_positive < 1
                and self.np_total[degree] > 10
            ):
                proportion = self.np_positive[degree] / self.np_total[degree]
                if cspeed > 0 and proportion > self.prob_positive:
                    continue
                if cspeed <= 0 and proportion < self.prob_positive:
                    continue

            self.np_total[degree] += 1
            if cspeed > 0:
                self.np_positive[degree] += 1
            break

        # # debug
        # logger.info(str(cspeed))
        # logger.info(str(cspeed) + "\t" + " ||||| ".join(str(s) for s in system[:3]))
        # print(degree, str(ngen) + " : " + str((ngen - degree) / ngen * 100.0))

        # encode input
        x = self.write_int(degree)
        for s in system:
            x.append(self.func_separator)
            x.extend(self.encode_expr(s))

        # encode output: eigenvalue, and optionally the Jacobian matrix
        eigenvalue = self.write_float(cspeed)
        if self.predict_jacobian:
            y = []
            for row in jacobian:
                for value in row:
                    y.extend(
                        self.write_complex(value, precision=self.jacobian_precision)
                    )
                    y.append(self.list_separator)
                y.append(self.line_separator)
            y.append(self.mtrx_separator)
            y.extend(eigenvalue)
        else:
            y = eigenvalue

        if return_system:
            return x, y, system
        else:
            return x, y