def _fastpolys(self): """ Lazy attribute for fast_callable polynomials for affine morphsims. EXAMPLES:: sage: P.<x,y> = AffineSpace(QQ, 2) sage: H = Hom(P, P) sage: f = H([x^2+y^2, y^2/(1+x)]) sage: [t.op_list() for g in f._fastpolys for t in g] [[('load_const', 0), ('load_const', 1), ('load_arg', 1), ('ipow', 2), 'mul', 'add', ('load_const', 1), ('load_arg', 0), ('ipow', 2), 'mul', 'add', 'return'], [('load_const', 0), ('load_const', 1), ('load_arg', 1), ('ipow', 2), 'mul', 'add', 'return'], [('load_const', 0), ('load_const', 1), 'add', 'return'], [('load_const', 0), ('load_const', 1), ('load_arg', 0), ('ipow', 1), 'mul', 'add', ('load_const', 1), 'add', 'return']] """ polys = self._polys R = self.domain().ambient_space().coordinate_ring() # fastpolys[0] corresponds to the numerator polys, fastpolys[1] corresponds to denominator polys fastpolys = [[], []] for poly in polys: # Determine if truly polynomials. Store the numerator and denominator as separate polynomials # and repeat the normal process for both. try: poly_numerator = R(poly) poly_denominator = R.one() except TypeError: poly_numerator = R(poly.numerator()) poly_denominator = R(poly.denominator()) # These tests are in place because the float and integer domain evaluate # faster than using the base_ring if self._is_prime_finite_field: prime = polys[0].base_ring().characteristic() degree = max(poly_numerator.degree(), poly_denominator.degree()) height = max( [abs(c.lift()) for c in poly_numerator.coefficients()] + [abs(c.lift()) for c in poly_denominator.coefficients()] ) num_terms = max(len(poly_numerator.coefficients()), len(poly_denominator.coefficients())) largest_value = num_terms * height * (prime - 1) ** degree # If the calculations will not overflow the float data type use domain float # Else use domain integer if largest_value < (2 ** sys.float_info.mant_dig): fastpolys[0].append(fast_callable(poly_numerator, domain=float)) fastpolys[1].append(fast_callable(poly_denominator, domain=float)) else: fastpolys[0].append(fast_callable(poly_numerator, domain=ZZ)) fastpolys[1].append(fast_callable(poly_denominator, domain=ZZ)) else: fastpolys[0].append(fast_callable(poly_numerator, domain=poly.base_ring())) fastpolys[1].append(fast_callable(poly_denominator, domain=poly.base_ring())) return fastpolys
def _fastpolys(self): """ Lazy attribute for fast_callable polynomials for ``self``. EXAMPLES:: sage: P.<x,y> = AffineSpace(QQ,2) sage: H = Hom(P,P) sage: f = H([x^2+y^2,y^2/(1+x)]) sage: [t.op_list() for g in f._fastpolys for t in g] [[('load_const', 0), ('load_const', 1), ('load_arg', 1), ('ipow', 2), 'mul', 'add', ('load_const', 1), ('load_arg', 0), ('ipow', 2), 'mul', 'add', 'return'], [('load_const', 0), ('load_const', 1), ('load_arg', 1), ('ipow', 2), 'mul', 'add', 'return'], [('load_const', 0), ('load_const', 1), 'add', 'return'], [('load_const', 0), ('load_const', 1), ('load_arg', 0), ('ipow', 1), 'mul', 'add', ('load_const', 1), 'add', 'return']] """ polys = self._polys R = self.domain().ambient_space().coordinate_ring() # fastpolys[0] corresponds to the numerator polys, fastpolys[1] corresponds to denominator polys fastpolys = [[], []] for poly in polys: # Determine if truly polynomials. Store the numerator and denominator as separate polynomials # and repeat the normal process for both. try: poly_numerator=R(poly) poly_denominator=R.one() except TypeError: poly_numerator=R(poly.numerator()) poly_denominator=R(poly.denominator()) # These tests are in place because the float and integer domain evaluate # faster than using the base_ring if self._is_prime_finite_field: prime = polys[0].base_ring().characteristic() degree = max(poly_numerator.degree(), poly_denominator.degree()) height = max([abs(c.lift()) for c in poly_numerator.coefficients()]\ + [abs(c.lift()) for c in poly_denominator.coefficients()]) num_terms = max(len(poly_numerator.coefficients()), len(poly_denominator.coefficients())) largest_value = num_terms * height * (prime - 1) ** degree # If the calculations will not overflow the float data type use domain float # Else use domain integer if largest_value < (2 ** sys.float_info.mant_dig): fastpolys[0].append(fast_callable(poly_numerator, domain=float)) fastpolys[1].append(fast_callable(poly_denominator, domain=float)) else: fastpolys[0].append(fast_callable(poly_numerator, domain=ZZ)) fastpolys[1].append(fast_callable(poly_denominator, domain=ZZ)) else: fastpolys[0].append(fast_callable(poly_numerator, domain=poly.base_ring())) fastpolys[1].append(fast_callable(poly_denominator, domain=poly.base_ring())) return fastpolys
def _eval_single_expr(self, expression): '''Evaluate one single expanded expression''' if not isinstance(expression, sage_expr): raise TypeError('_eval_single_expr takes only single expressions') arglist = [] vallist = [] args = expression.arguments() #print (expression) # DEBUG # loop over arguments found = False # an indicator that unresolved peTab-related arguments are found for arg in args: str_arg = repr(arg) #print (repr(str_arg)) # DEBUG if str_arg in self: # found 'our' argument found = True arglist.append(arg) if isinstance(self[str_arg], peScriptRef): vallist.append(self[str_arg].value) else: vallist.append(self[str_arg]) elif str_arg in unitdict: # found a unit, which is not overridden by parameter name found = True arglist.append(arg) vallist.append(unitdict[str_arg]) else: # found unknown argument, leave it as is arglist.append(arg) vallist.append(arg) #print (arglist) #DEBUG # check if we have found 'our' unresolved arguments #print (arglist) #DEBUG #print (vallist) #DEBUG if (found): from sage.ext.fast_callable import fast_callable pExpr = fast_callable(expression, vars=arglist) #print (pExpr(*(vallist))) #debug return pExpr(*(vallist)) else: # no unresolved arguments - the end return expression
def subexpressions_list(f, pars=None): """ Construct the lists with the intermediate steps on the evaluation of the function. INPUT: - ``f`` -- a symbolic function of several components. - ``pars`` -- a list of the parameters that appear in the function this should be the symbolic constants that appear in f but are not arguments. OUTPUT: - a list of the intermediate subexpressions that appear in the evaluation of f. - a list with the operations used to construct each of the subexpressions. each element of this list is a tuple, formed by a string describing the operation made, and the operands. For the trigonometric functions, some extra expressions will be added. These extra expressions will be used later to compute their derivatives. EXAMPLES:: sage: from sage.interfaces.tides import subexpressions_list sage: var('x,y') (x, y) sage: f(x,y) = [x^2+y, cos(x)/log(y)] sage: subexpressions_list(f) ([x^2, x^2 + y, sin(x), cos(x), log(y), cos(x)/log(y)], [('mul', x, x), ('add', y, x^2), ('sin', x), ('cos', x), ('log', y), ('div', log(y), cos(x))]) :: sage: f(a)=[cos(a), arctan(a)] sage: from sage.interfaces.tides import subexpressions_list sage: subexpressions_list(f) ([sin(a), cos(a), a^2, a^2 + 1, arctan(a)], [('sin', a), ('cos', a), ('mul', a, a), ('add', 1, a^2), ('atan', a)]) :: sage: from sage.interfaces.tides import subexpressions_list sage: var('s,b,r') (s, b, r) sage: f(t,x,y,z)= [s*(y-x),x*(r-z)-y,x*y-b*z] sage: subexpressions_list(f,[s,b,r]) ([-y, x - y, s*(x - y), -s*(x - y), -z, r - z, (r - z)*x, -y, (r - z)*x - y, x*y, b*z, -b*z, x*y - b*z], [('mul', -1, y), ('add', -y, x), ('mul', x - y, s), ('mul', -1, s*(x - y)), ('mul', -1, z), ('add', -z, r), ('mul', x, r - z), ('mul', -1, y), ('add', -y, (r - z)*x), ('mul', y, x), ('mul', z, b), ('mul', -1, b*z), ('add', -b*z, x*y)]) :: sage: var('x, y') (x, y) sage: f(x,y)=[exp(x^2+sin(y))] sage: from sage.interfaces.tides import * sage: subexpressions_list(f) ([x^2, sin(y), cos(y), x^2 + sin(y), e^(x^2 + sin(y))], [('mul', x, x), ('sin', y), ('cos', y), ('add', sin(y), x^2), ('exp', x^2 + sin(y))]) """ from sage.functions.trig import sin, cos, arcsin, arctan, arccos variables = f[0].arguments() if not pars: parameters = [] else: parameters = pars varpar = list(parameters) + list(variables) F = symbolic_expression([i(*variables) for i in f]).function(*varpar) lis = flatten([fast_callable(i,vars=varpar).op_list() for i in F], max_level=1) stack = [] const =[] stackcomp=[] detail=[] for i in lis: if i[0] == 'load_arg': stack.append(varpar[i[1]]) elif i[0] == 'ipow': if i[1] in NN: basis = stack[-1] for j in range(i[1]-1): a=stack.pop(-1) detail.append(('mul', a, basis)) stack.append(a*basis) stackcomp.append(stack[-1]) else: detail.append(('pow',stack[-1],i[1])) stack[-1]=stack[-1]**i[1] stackcomp.append(stack[-1]) elif i[0] == 'load_const': const.append(i[1]) stack.append(i[1]) elif i == 'mul': a=stack.pop(-1) b=stack.pop(-1) detail.append(('mul', a, b)) stack.append(a*b) stackcomp.append(stack[-1]) elif i == 'div': a=stack.pop(-1) b=stack.pop(-1) detail.append(('div', a, b)) stack.append(b/a) stackcomp.append(stack[-1]) elif i == 'add': a=stack.pop(-1) b=stack.pop(-1) detail.append(('add',a,b)) stack.append(a+b) stackcomp.append(stack[-1]) elif i == 'pow': a=stack.pop(-1) b=stack.pop(-1) detail.append(('pow', b, a)) stack.append(b**a) stackcomp.append(stack[-1]) elif i[0] == 'py_call' and str(i[1])=='log': a=stack.pop(-1) detail.append(('log', a)) stack.append(log(a)) stackcomp.append(stack[-1]) elif i[0] == 'py_call' and str(i[1])=='exp': a=stack.pop(-1) detail.append(('exp', a)) stack.append(exp(a)) stackcomp.append(stack[-1]) elif i[0] == 'py_call' and str(i[1])=='sin': a=stack.pop(-1) detail.append(('sin', a)) detail.append(('cos', a)) stackcomp.append(sin(a)) stackcomp.append(cos(a)) stack.append(sin(a)) elif i[0] == 'py_call' and str(i[1])=='cos': a=stack.pop(-1) detail.append(('sin', a)) detail.append(('cos', a)) stackcomp.append(sin(a)) stackcomp.append(cos(a)) stack.append(cos(a)) elif i[0] == 'py_call' and str(i[1])=='tan': a=stack.pop(-1) b = sin(a) c = cos(a) detail.append(('sin', a)) detail.append(('cos', a)) detail.append(('div', b, c)) stackcomp.append(b) stackcomp.append(c) stackcomp.append(b/c) stack.append(b/c) elif i[0] == 'py_call' and str(i[1])=='arctan': a=stack.pop(-1) detail.append(('mul', a, a)) detail.append(('add', 1, a*a)) detail.append(('atan', a)) stackcomp.append(a*a) stackcomp.append(1+a*a) stackcomp.append(arctan(a)) stack.append(arctan(a)) elif i[0] == 'py_call' and str(i[1])=='arcsin': a=stack.pop(-1) detail.append(('mul', a, a)) detail.append(('mul', -1, a*a)) detail.append(('add', 1, -a*a)) detail.append(('pow', 1- a*a, 0.5)) detail.append(('asin', a)) stackcomp.append(a*a) stackcomp.append(-a*a) stackcomp.append(1-a*a) stackcomp.append(sqrt(1-a*a)) stackcomp.append(arcsin(a)) stack.append(arcsin(a)) elif i[0] == 'py_call' and str(i[1])=='arccos': a=stack.pop(-1) detail.append(('mul', a, a)) detail.append(('mul', -1, a*a)) detail.append(('add', 1, -a*a)) detail.append(('pow', 1- a*a, 0.5)) detail.append(('mul', -1, sqrt(1-a*a))) detail.append(('acos', a)) stackcomp.append(a*a) stackcomp.append(-a*a) stackcomp.append(1-a*a) stackcomp.append(sqrt(1-a*a)) stackcomp.append(-sqrt(1-a*a)) stackcomp.append(arccos(a)) stack.append(arccos(a)) elif i[0] == 'py_call' and 'sqrt' in str(i[1]): a=stack.pop(-1) detail.append(('pow', a, 0.5)) stackcomp.append(sqrt(a)) stack.append(sqrt(a)) elif i == 'neg': a = stack.pop(-1) detail.append(('mul', -1, a)) stack.append(-a) stackcomp.append(-a) return stackcomp,detail
def find_fit(data, model, initial_guess=None, parameters=None, variables=None, solution_dict=False): r""" Finds numerical estimates for the parameters of the function model to give a best fit to data. INPUT: - ``data`` -- A two dimensional table of floating point numbers of the form `[[x_{1,1}, x_{1,2}, \ldots, x_{1,k}, f_1], [x_{2,1}, x_{2,2}, \ldots, x_{2,k}, f_2], \ldots, [x_{n,1}, x_{n,2}, \ldots, x_{n,k}, f_n]]` given as either a list of lists, matrix, or numpy array. - ``model`` -- Either a symbolic expression, symbolic function, or a Python function. ``model`` has to be a function of the variables `(x_1, x_2, \ldots, x_k)` and free parameters `(a_1, a_2, \ldots, a_l)`. - ``initial_guess`` -- (default: ``None``) Initial estimate for the parameters `(a_1, a_2, \ldots, a_l)`, given as either a list, tuple, vector or numpy array. If ``None``, the default estimate for each parameter is `1`. - ``parameters`` -- (default: ``None``) A list of the parameters `(a_1, a_2, \ldots, a_l)`. If model is a symbolic function it is ignored, and the free parameters of the symbolic function are used. - ``variables`` -- (default: ``None``) A list of the variables `(x_1, x_2, \ldots, x_k)`. If model is a symbolic function it is ignored, and the variables of the symbolic function are used. - ``solution_dict`` -- (default: ``False``) if ``True``, return the solution as a dictionary rather than an equation. EXAMPLES: First we create some data points of a sine function with some "random" perturbations:: sage: set_random_seed(0) sage: data = [(i, 1.2 * sin(0.5*i-0.2) + 0.1 * normalvariate(0, 1)) for i in xsrange(0, 4*pi, 0.2)] sage: var('a, b, c, x') (a, b, c, x) We define a function with free parameters `a`, `b` and `c`:: sage: model(x) = a * sin(b * x - c) We search for the parameters that give the best fit to the data:: sage: find_fit(data, model) [a == 1.21..., b == 0.49..., c == 0.19...] We can also use a Python function for the model:: sage: def f(x, a, b, c): return a * sin(b * x - c) sage: fit = find_fit(data, f, parameters = [a, b, c], variables = [x], solution_dict = True) sage: fit[a], fit[b], fit[c] (1.21..., 0.49..., 0.19...) We search for a formula for the `n`-th prime number:: sage: dataprime = [(i, nth_prime(i)) for i in range(1, 5000, 100)] sage: find_fit(dataprime, a * x * log(b * x), parameters = [a, b], variables = [x]) [a == 1.11..., b == 1.24...] ALGORITHM: Uses ``scipy.optimize.leastsq`` which in turn uses MINPACK's lmdif and lmder algorithms. """ import numpy if not isinstance(data, numpy.ndarray): try: data = numpy.array(data, dtype=float) except (ValueError, TypeError): raise TypeError( "data has to be a list of lists, a matrix, or a numpy array") elif data.dtype == object: raise ValueError("the entries of data have to be of type float") if data.ndim != 2: raise ValueError( "data has to be a two dimensional table of floating point numbers") from sage.structure.element import Expression if isinstance(model, Expression): if variables is None: variables = list(model.arguments()) if parameters is None: parameters = list(model.variables()) for v in variables: parameters.remove(v) if data.shape[1] != len(variables) + 1: raise ValueError( "each row of data needs %d entries, only %d entries given" % (len(variables) + 1, data.shape[1])) if parameters is None or len(parameters) == 0 or \ variables is None or len(variables) == 0: raise ValueError("no variables given") if initial_guess is None: initial_guess = len(parameters) * [1] if not isinstance(initial_guess, numpy.ndarray): try: initial_guess = numpy.array(initial_guess, dtype=float) except (ValueError, TypeError): raise TypeError( "initial_guess has to be a list, tuple, or numpy array") elif initial_guess.dtype == object: raise ValueError( "the entries of initial_guess have to be of type float") if len(initial_guess) != len(parameters): raise ValueError( "length of initial_guess does not coincide with the number of parameters" ) if isinstance(model, Expression): from sage.ext.fast_callable import fast_callable var_list = variables + parameters func = fast_callable(model, vars=var_list, domain=float) else: func = model def function(x_data, params): result = numpy.zeros(len(x_data)) for row in range(len(x_data)): fparams = numpy.hstack((x_data[row], params)).tolist() result[row] = func(*fparams) return result def error_function(params, x_data, y_data): result = numpy.zeros(len(x_data)) for row in range(len(x_data)): fparams = x_data[row].tolist() + params.tolist() result[row] = func(*fparams) return result - y_data x_data = data[:, 0:len(variables)] y_data = data[:, -1] from scipy.optimize import leastsq estimated_params, d = leastsq(error_function, initial_guess, args=(x_data, y_data)) if isinstance(estimated_params, float): estimated_params = [estimated_params] else: estimated_params = estimated_params.tolist() if solution_dict: dict = {} for item in zip(parameters, estimated_params): dict[item[0]] = item[1] return dict return [item[0] == item[1] for item in zip(parameters, estimated_params)]
def minimize_constrained(func, cons, x0, gradient=None, algorithm='default', **args): r""" Minimize a function with constraints. INPUT: - ``func`` -- Either a symbolic function, or a Python function whose argument is a tuple with n components - ``cons`` -- constraints. This should be either a function or list of functions that must be positive. Alternatively, the constraints can be specified as a list of intervals that define the region we are minimizing in. If the constraints are specified as functions, the functions should be functions of a tuple with `n` components (assuming `n` variables). If the constraints are specified as a list of intervals and there are no constraints for a given variable, that component can be (``None``, ``None``). - ``x0`` -- Initial point for finding minimum - ``algorithm`` -- Optional, specify the algorithm to use: - ``'default'`` -- default choices - ``'l-bfgs-b'`` -- only effective if you specify bound constraints. See [ZBN1997]_. - ``gradient`` -- Optional gradient function. This will be computed automatically for symbolic functions. This is only used when the constraints are specified as a list of intervals. EXAMPLES: Let us maximize `x + y - 50` subject to the following constraints: `50x + 24y \leq 2400`, `30x + 33y \leq 2100`, `x \geq 45`, and `y \geq 5`:: sage: y = var('y') sage: f = lambda p: -p[0]-p[1]+50 sage: c_1 = lambda p: p[0]-45 sage: c_2 = lambda p: p[1]-5 sage: c_3 = lambda p: -50*p[0]-24*p[1]+2400 sage: c_4 = lambda p: -30*p[0]-33*p[1]+2100 sage: a = minimize_constrained(f,[c_1,c_2,c_3,c_4],[2,3]) sage: a (45.0, 6.25...) Let's find a minimum of `\sin(xy)`:: sage: x,y = var('x y') sage: f = sin(x*y) sage: minimize_constrained(f, [(None,None),(4,10)],[5,5]) (4.8..., 4.8...) Check if L-BFGS-B finds the same minimum:: sage: minimize_constrained(f, [(None,None),(4,10)],[5,5], algorithm='l-bfgs-b') (4.7..., 4.9...) Rosenbrock function (see the :wikipedia:`Rosenbrock_function`):: sage: from scipy.optimize import rosen, rosen_der sage: minimize_constrained(rosen, [(-50,-10),(5,10)],[1,1],gradient=rosen_der,algorithm='l-bfgs-b') (-10.0, 10.0) sage: minimize_constrained(rosen, [(-50,-10),(5,10)],[1,1],algorithm='l-bfgs-b') (-10.0, 10.0) TESTS: Check if :trac:`6592` is fixed:: sage: x, y = var('x y') sage: f = (100 - x) + (1000 - y) sage: c = x + y - 479 # > 0 sage: minimize_constrained(f, [c], [100, 300]) (805.985..., 1005.985...) sage: minimize_constrained(f, c, [100, 300]) (805.985..., 1005.985...) """ from sage.structure.element import Expression from sage.ext.fast_callable import fast_callable import numpy from scipy import optimize function_type = type(lambda x, y: x + y) if isinstance(func, Expression): var_list = func.variables() fast_f = fast_callable(func, vars=var_list, domain=float) f = lambda p: fast_f(*p) gradient_list = func.gradient() fast_gradient_functions = [ fast_callable(gi, vars=var_list, domain=float) for gi in gradient_list ] gradient = lambda p: numpy.array( [a(*p) for a in fast_gradient_functions]) if isinstance(cons, Expression): fast_cons = fast_callable(cons, vars=var_list, domain=float) cons = lambda p: numpy.array([fast_cons(*p)]) elif isinstance(cons, list) and isinstance(cons[0], Expression): fast_cons = [ fast_callable(ci, vars=var_list, domain=float) for ci in cons ] cons = lambda p: numpy.array([a(*p) for a in fast_cons]) else: f = func if isinstance(cons, list): if isinstance(cons[0], tuple) or isinstance(cons[0], list) or cons[0] is None: if gradient is not None: if algorithm == 'l-bfgs-b': min = optimize.fmin_l_bfgs_b(f, x0, gradient, bounds=cons, iprint=-1, **args)[0] else: min = optimize.fmin_tnc(f, x0, gradient, bounds=cons, messages=0, **args)[0] else: if algorithm == 'l-bfgs-b': min = optimize.fmin_l_bfgs_b(f, x0, approx_grad=True, bounds=cons, iprint=-1, **args)[0] else: min = optimize.fmin_tnc(f, x0, approx_grad=True, bounds=cons, messages=0, **args)[0] elif isinstance(cons[0], function_type) or isinstance( cons[0], Expression): min = optimize.fmin_cobyla(f, x0, cons, **args) elif isinstance(cons, function_type) or isinstance(cons, Expression): min = optimize.fmin_cobyla(f, x0, cons, **args) return vector(RDF, min)
def minimize(func, x0, gradient=None, hessian=None, algorithm="default", verbose=False, **args): r""" This function is an interface to a variety of algorithms for computing the minimum of a function of several variables. INPUT: - ``func`` -- Either a symbolic function or a Python function whose argument is a tuple with `n` components - ``x0`` -- Initial point for finding minimum. - ``gradient`` -- Optional gradient function. This will be computed automatically for symbolic functions. For Python functions, it allows the use of algorithms requiring derivatives. It should accept a tuple of arguments and return a NumPy array containing the partial derivatives at that point. - ``hessian`` -- Optional hessian function. This will be computed automatically for symbolic functions. For Python functions, it allows the use of algorithms requiring derivatives. It should accept a tuple of arguments and return a NumPy array containing the second partial derivatives of the function. - ``algorithm`` -- String specifying algorithm to use. Options are ``'default'`` (for Python functions, the simplex method is the default) (for symbolic functions bfgs is the default): - ``'simplex'`` -- using the downhill simplex algorithm - ``'powell'`` -- use the modified Powell algorithm - ``'bfgs'`` -- (Broyden-Fletcher-Goldfarb-Shanno) requires gradient - ``'cg'`` -- (conjugate-gradient) requires gradient - ``'ncg'`` -- (newton-conjugate gradient) requires gradient and hessian - ``verbose`` -- (optional, default: False) print convergence message .. NOTE:: For additional information on the algorithms implemented in this function, consult SciPy's `documentation on optimization and root finding <https://docs.scipy.org/doc/scipy/reference/optimize.html>`_ EXAMPLES: Minimize a fourth order polynomial in three variables (see the :wikipedia:`Rosenbrock_function`):: sage: vars = var('x y z') sage: f = 100*(y-x^2)^2+(1-x)^2+100*(z-y^2)^2+(1-y)^2 sage: minimize(f, [.1,.3,.4]) # abs tol 1e-6 (1.0, 1.0, 1.0) Try the newton-conjugate gradient method; the gradient and hessian are computed automatically:: sage: minimize(f, [.1, .3, .4], algorithm="ncg") # abs tol 1e-6 (1.0, 1.0, 1.0) We get additional convergence information with the `verbose` option:: sage: minimize(f, [.1, .3, .4], algorithm="ncg", verbose=True) Optimization terminated successfully. ... (0.9999999..., 0.999999..., 0.999999...) Same example with just Python functions:: sage: def rosen(x): # The Rosenbrock function ....: return sum(100.0r*(x[1r:]-x[:-1r]**2.0r)**2.0r + (1r-x[:-1r])**2.0r) sage: minimize(rosen, [.1,.3,.4]) # abs tol 3e-5 (1.0, 1.0, 1.0) Same example with a pure Python function and a Python function to compute the gradient:: sage: def rosen(x): # The Rosenbrock function ....: return sum(100.0r*(x[1r:]-x[:-1r]**2.0r)**2.0r + (1r-x[:-1r])**2.0r) sage: import numpy sage: from numpy import zeros sage: def rosen_der(x): ....: xm = x[1r:-1r] ....: xm_m1 = x[:-2r] ....: xm_p1 = x[2r:] ....: der = zeros(x.shape, dtype=float) ....: der[1r:-1r] = 200r*(xm-xm_m1**2r) - 400r*(xm_p1 - xm**2r)*xm - 2r*(1r-xm) ....: der[0] = -400r*x[0r]*(x[1r]-x[0r]**2r) - 2r*(1r-x[0]) ....: der[-1] = 200r*(x[-1r]-x[-2r]**2r) ....: return der sage: minimize(rosen, [.1,.3,.4], gradient=rosen_der, algorithm="bfgs") # abs tol 1e-6 (1.0, 1.0, 1.0) """ from sage.structure.element import Expression from sage.ext.fast_callable import fast_callable import numpy from scipy import optimize if isinstance(func, Expression): var_list = func.variables() var_names = [str(_) for _ in var_list] fast_f = fast_callable(func, vars=var_names, domain=float) f = lambda p: fast_f(*p) gradient_list = func.gradient() fast_gradient_functions = [ fast_callable(gradient_list[i], vars=var_names, domain=float) for i in range(len(gradient_list)) ] gradient = lambda p: numpy.array( [a(*p) for a in fast_gradient_functions]) else: f = func if algorithm == "default": if gradient is None: min = optimize.fmin(f, [float(_) for _ in x0], disp=verbose, **args) else: min = optimize.fmin_bfgs(f, [float(_) for _ in x0], fprime=gradient, disp=verbose, **args) else: if algorithm == "simplex": min = optimize.fmin(f, [float(_) for _ in x0], disp=verbose, **args) elif algorithm == "bfgs": min = optimize.fmin_bfgs(f, [float(_) for _ in x0], fprime=gradient, disp=verbose, **args) elif algorithm == "cg": min = optimize.fmin_cg(f, [float(_) for _ in x0], fprime=gradient, disp=verbose, **args) elif algorithm == "powell": min = optimize.fmin_powell(f, [float(_) for _ in x0], disp=verbose, **args) elif algorithm == "ncg": if isinstance(func, Expression): hess = func.hessian() hess_fast = [[ fast_callable(a, vars=var_names, domain=float) for a in row ] for row in hess] hessian = lambda p: [[a(*p) for a in row] for row in hess_fast] from scipy import dot hessian_p = lambda p, v: dot(numpy.array(hessian(p)), v) min = optimize.fmin_ncg(f, [float(_) for _ in x0], fprime=gradient, \ fhess=hessian, fhess_p=hessian_p, disp=verbose, **args) return vector(RDF, min)
def createLists(f, pars): ''' Creates list1 and list 2 from the right side function f of an ODE: input: f -> right side function for ODE system pars -> list with the parameters on f output: list1 and list2 Example with Lorenz Equation sage: var('t, x, y, z') # variables for lorenz equations sage: var('s, r, b') # parameters for lorenz equations sage: f(t,x,y,z) = [s*(y-x), x*(r-z) - y, x*y - b*z] # Right side function for Lorenz equation ''' vars = f[0].arguments() # gets the list of variables from function f varpar = list(vars) + list(pars) # gets the list of vars and pars totegher _f = f(*vars).function( varpar) # _f is f but with vars and pars as arguments fastCallList = flatten( [fast_callable(i, vars=varpar).op_list() for i in f], max_level=1) # This list is the fast callable version of f using a stack-mode call ''' We create create the lists list1, list2 and stack. stack will be expresion stack-list ''' list1 = [] list2 = [] stack = [] ''' Starts parser on fastCallList. ''' for s in fastCallList: if s[0] == 'load_arg': # Loads a variable in stack. no changes on list1, or list2 stack.append(varpar[ s[1]]) # appends the variable or parameter on symbolic stack elif s[0] == 'ipow': # Integer power. if s[1] in NN: # If natural, parser as products basis = stack[-1] for j in range(s[1] - 1): a = stack.pop(-1) stack.append(a * basis) list1.append(stack[-1]) list2.append(('mul', a, basis)) elif -s[1] in NN: basis = stack[-1] for j in range(-s[1] - 1): a = stack.pop(-1) stack.append(a * basis) list1.append(stack[-1]) list2.append(('mul', a, basis)) a = stack.pop(-1) stack.append(1 / a) list1.append(stack[-1]) list2.append(('div', 1, a)) else: # Attach as normal power a = stack.pop(-1) #basis stack.append(a**s[1]) list1.append(stack[-1]) list2.append(('pow', a, s[1])) elif s[0] == 'load_const': # Loads a constant value on stack. Not in list1 or list2 stack.append(s[1]) elif s == 'neg': # multiplies by -1.0 a = stack.pop(-1) # expresion to be multiplied by -1 stack.append(-a) list1.append(stack[-1]) list2.append(('mul', -1, a)) elif s == 'mul': # Product a = stack.pop(-1) b = stack.pop(-1) list2.append(('mul', a, b)) stack.append(a * b) list1.append(stack[-1]) elif s == 'div': # divission Numerator First. b = stack.pop(-1) # denominator (after a in stack) a = stack.pop(-1) # numerator (before b in stack) if expresionIsConstant(b, pars): list1.append(1 / b) list2.append(('div', 1, b)) b = 1 / b stack.append(a * b) list1.append(stack[-1]) list2.append(('mul', a, b)) else: list2.append(('div', a, b)) stack.append(a / b) list1.append(stack[-1]) elif s == 'add': # addition b = stack.pop(-1) # second operand a = stack.pop(-1) # first operand stack.append(a + b) list1.append(stack[-1]) list2.append(('add', a, b)) elif s == 'pow': # any other pow b = stack.pop(-1) # exponent a = stack.pop(-1) # basis stack.append(a**b) list1.append(stack[-1]) list2.append(('pow', a, b)) elif s[0] == 'py_call' and 'sqrt' in str( s[1]): # square root. Compute as power a = stack.pop(-1) # argument of sqrt stack.append(sqrt(a)) list1.append(stack[-1]) list2.append(('pow', a, 0.5)) elif s[0] == 'py_call' and str(s[1]) == 'log': # logarithm a = stack.pop(-1) # argument of log stack.append(log(a)) list1.append(stack[-1]) list2.append(('log', a)) elif s[0] == 'py_call' and str(s[1]) == 'exp': a = stack.pop(-1) # argument of exp stack.append(exp(a)) list1.append(stack[-1]) list2.append(('exp', a)) elif s[0] == 'py_call' and str( s[1]) == 'sin': # sine. For AD needs computation of cos a = stack.pop(-1) stack.append(sin(a)) list1.append(sin(a)) list1.append(cos(a)) list2.append(('sin', a)) list2.append(('cos', a)) elif s[0] == 'py_call' and str( s[1]) == 'cos': # cosine. For AD needs computation of sin a = stack.pop(-1) stack.append(cos(a)) list1.append(sin(a)) list1.append(cos(a)) list2.append(('sin', a)) list2.append(('cos', a)) elif s[0] == 'py_call' and str(s[1]) == 'tan': a = stack.pop(-1) stack.append(tan(a)) list1.append(sin(a)) list1.append(cos(a)) list1.append(tan(a)) list2.append(('sin', a)) list2.append(('cos', a)) list2.append(('div', sin(a), cos(a))) return list1, list2
def createLists (f, pars): ''' Creates list1 and list 2 from the right side function f of an ODE: input: f -> right side function for ODE system pars -> list with the parameters on f output: list1 and list2 Example with Lorenz Equation sage: var('t, x, y, z') # variables for lorenz equations sage: var('s, r, b') # parameters for lorenz equations sage: f(t,x,y,z) = [s*(y-x), x*(r-z) - y, x*y - b*z] # Right side function for Lorenz equation ''' vars = f[0].arguments () # gets the list of variables from function f varpar = list (vars) + list (pars) # gets the list of vars and pars totegher _f = f (*vars).function (varpar) # _f is f but with vars and pars as arguments fastCallList = flatten ([fast_callable (i,vars=varpar).op_list () for i in f], max_level=1) # This list is the fast callable version of f using a stack-mode call ''' We create create the lists list1, list2 and stack. stack will be expresion stack-list ''' list1 = []; list2 = []; stack = []; ''' Starts parser on fastCallList. ''' for s in fastCallList: if s[0] == 'load_arg': # Loads a variable in stack. no changes on list1, or list2 stack.append (varpar[s[1]]) # appends the variable or parameter on symbolic stack elif s[0] == 'ipow': # Integer power. if s[1] in NN: # If natural, parser as products basis = stack[-1] for j in range (s[1]-1): a=stack.pop (-1) stack.append (a*basis) list1.append (stack[-1]) list2.append (('mul', a, basis)) elif -s[1] in NN: basis = stack[-1] for j in range (-s[1]-1): a=stack.pop (-1) stack.append (a*basis) list1.append (stack[-1]) list2.append (('mul', a, basis)) a = stack.pop (-1); stack.append (1/a); list1.append (stack[-1]) list2.append (('div', 1, a)) else: # Attach as normal power a = stack.pop (-1) #basis stack.append (a ** s[1]) list1.append (stack[-1]) list2.append (('pow', a, s[1])) elif s[0] == 'load_const': # Loads a constant value on stack. Not in list1 or list2 stack.append (s[1]) elif s == 'neg': # multiplies by -1.0 a = stack.pop (-1) # expresion to be multiplied by -1 stack.append (-a) list1.append (stack[-1]) list2.append (('mul', -1, a)) elif s == 'mul': # Product a=stack.pop (-1) b=stack.pop (-1) list2.append (('mul', a, b)) stack.append (a*b) list1.append (stack[-1]) elif s == 'div': # divission Numerator First. b=stack.pop (-1) # denominator (after a in stack) a=stack.pop (-1) # numerator (before b in stack) if expresionIsConstant (b, pars): list1.append(1/b) list2.append(('div', 1, b)) b = 1/b; stack.append (a*b) list1.append(stack[-1]) list2.append (('mul', a, b)) else: list2.append (('div', a, b)) stack.append (a/b) list1.append (stack[-1]) elif s == 'add': # addition b = stack.pop (-1) # second operand a = stack.pop (-1) # first operand stack.append (a+b) list1.append (stack[-1]) list2.append (('add', a, b)) elif s == 'pow': # any other pow b = stack.pop (-1) # exponent a = stack.pop (-1) # basis stack.append (a**b) list1.append (stack[-1]) list2.append (('pow', a, b)) elif s[0] == 'py_call' and 'sqrt' in str (s[1]): # square root. Compute as power a = stack.pop (-1) # argument of sqrt stack.append (sqrt (a)) list1.append (stack[-1]) list2.append (('pow', a, 0.5)) elif s[0] == 'py_call' and str (s[1]) == 'log': # logarithm a = stack.pop (-1); # argument of log stack.append (log (a)) list1.append (stack[-1]) list2.append (('log', a)) elif s[0] == 'py_call' and str (s[1]) == 'exp': a = stack.pop (-1); # argument of exp stack.append (exp (a)) list1.append (stack[-1]) list2.append (('exp', a)) elif s[0] == 'py_call' and str (s[1]) == 'sin': # sine. For AD needs computation of cos a = stack.pop (-1) stack.append (sin (a)) list1.append (sin (a)) list1.append (cos (a)) list2.append (('sin', a)) list2.append (('cos', a)) elif s[0] == 'py_call' and str (s[1]) == 'cos': # cosine. For AD needs computation of sin a = stack.pop (-1) stack.append (cos (a)) list1.append (sin (a)) list1.append (cos (a)) list2.append (('sin', a)) list2.append (('cos', a)) elif s[0] == 'py_call' and str (s[1]) == 'tan': a = stack.pop (-1) stack.append (tan (a)) list1.append (sin (a)) list1.append (cos (a)) list1.append (tan (a)) list2.append (('sin', a)) list2.append (('cos', a)) list2.append (('div', sin (a), cos (a))) return list1, list2
def desolve_mintides(f, ics, initial, final, delta, tolrel=1e-16, tolabs=1e-16): r""" Solve numerically a system of first order differential equations using the taylor series integrator implemeted in mintides. INPUT: - ``f`` - symboli """ # get the list of operators called from sage.misc.misc import SAGE_ROOT RR = RealField() variables = f[0].arguments() nvars = len(variables) lis = flatten([fast_callable(i,vars=variables).op_list() for i in f], max_level=1) deflist = [] stack = [] const =[] stackcomp=[] detail=[] for i in lis: if i[0] == 'load_arg': stack.append(variables[i[1]]) elif i[0] == 'ipow': if i[1] in NN: basis = stack[-1] for j in range(i[1]-1): a=stack.pop(-1) detail.append(('mul', a, basis)) stack.append(a*basis) stackcomp.append(stack[-1]) else: detail.append(('pow',stack[-1],i[1])) stack[-1]=stack[-1]**i[1] stackcomp.append(stack[-1]) elif i[0] == 'load_const': const.append(i[1]) stack.append(i[1]) elif i == 'mul': a=stack.pop(-1) b=stack.pop(-1) detail.append(('mul', a, b)) stack.append(a*b) stackcomp.append(stack[-1]) elif i == 'div': a=stack.pop(-1) b=stack.pop(-1) detail.append(('div', a, b)) stack.append(b/a) stackcomp.append(stack[-1]) elif i == 'add': a=stack.pop(-1) b=stack.pop(-1) detail.append(('add',a,b)) stack.append(a+b) stackcomp.append(stack[-1]) elif i == 'pow': a=stack.pop(-1) b=stack.pop(-1) detail.append(('pow', b, a)) stack.append(b**a) stackcomp.append(stack[-1]) elif i[0] == 'py_call' and str(i[1])=='log': a=stack.pop(-1) detail.append(('log', a)) stack.append(log(a)) stackcomp.append(stack[-1]) elif i[0] == 'py_call' and str(i[1])=='exp': a=stack.pop(-1) detail.append(('exp', a)) stack.append(exp(a)) stackcomp.append(stack[-1]) elif i[0] == 'py_call' and str(i[1])=='sin': a=stack.pop(-1) detail.append(('sin', a)) detail.append(('cos', a)) stackcomp.append(sin(a)) stackcomp.append(cos(a)) stack.append(sin(a)) elif i[0] == 'py_call' and str(i[1])=='cos': a=stack.pop(-1) detail.append(('sin', a)) detail.append(('cos', a)) stackcomp.append(sin(a)) stackcomp.append(cos(a)) stack.append(cos(a)) elif i == 'neg': a = stack.pop(-1) detail.append(('mul', -1, a)) stack.append(-a) stackcomp.append(-a) l1, l2 = stackcomp, detail #remove the repeated expressions for i in range(len(l1)-1): j=i+1 while j<len(l1): if l1[j] == l1[i]: l1.pop(j) l2.pop(j) else: j+=1 #remove the constants i=0 while i < len(l1): if l1[i] in RR: l1.pop(i) l2.pop(i) else: i+=1 #generate the corresponding c lines l3=[] var = f[0].arguments() for i in l2: oper = i[0] if oper in ["log", "exp", "sin", "cos"]: a = i[1] if a in var: l3.append((oper, 'XX[{}]'.format(var.index(a)))) elif a in l1: l3.append((oper, 'XX[{}]'.format(l1.index(a)+len(var)))) else: a=i[1] b=i[2] consta=False constb=False if a in var: aa = 'XX[{}]'.format(var.index(a)) elif a in l1: aa = 'XX[{}]'.format(l1.index(a)+len(var)) else: consta=True aa = str(a) if b in var: bb = 'XX[{}]'.format(var.index(b)) elif b in l1: bb = 'XX[{}]'.format(l1.index(b)+len(var)) else: constb=True bb = str(b) if consta: oper += '_c' if not oper=='div': bb, aa = aa,bb elif constb: oper += '_c' l3.append((oper, aa, bb)) n = len(variables) res = [] for i in range(len(l3)): el = l3[i] string = "XX[{}][i] = ".format(i + n) if el[0] == 'add': string += el[1] + "[i] + " + el[2] +"[i];" elif el[0] == 'add_c': string += "(i==0)? {}+".format(N(el[2])) + el[1] + "[0] : "+ el[1]+ "[i];" elif el[0] == 'mul': string += "mul_mc("+el[1]+","+el[2]+",i);" elif el[0] == 'mul_c': string += str(N(el[2])) + "*"+ el[1] + "[i];" elif el[0] == 'pow_c': string += "pow_mc_c("+el[1]+","+str(N(el[2]))+",XX[{}], i);".format(i+n) elif el[0] == 'div': string += "div_mc_c("+el[2]+","+el[1]+",XX[{}], i);".format(i+n) elif el[0] == 'div_c': string += "inv_mc_c("+str(N(el[2]))+","+el[1]+",XX[{}], i);".format(i+n) elif el[0] == 'log': string += "log_mc("+el[1]+",XX[{}], i);".format(i+n) elif el[0] == 'exp': string += "exp_mc("+el[1]+",XX[{}], i);".format(i+n) elif el[0] == 'sin': string += "sin_mc("+el[1]+",XX[{}], i);".format(i+n+1) elif el[0] == 'cos': string += "cos_mc("+el[1]+",XX[{}], i);".format(i+n-1) res.append(string) l1 = list(variables)+l1 indices = [l1.index(i(*variables))+n for i in f] for i in range (1,len(variables)): res.append("XX[{}][i+1] = XX[{}][i] / (i+1.0);".format(i,indices[i-1]-len(variables))) code = res VAR = len(variables) PAR =0 TT = len(res) tempdir = mkdtemp() fname = tempdir + '/integrator.c' shutil.copy(SAGE_ROOT+'/src/sage/calculus/tides/seriesFile00.txt', fname) outfile = open(fname, 'a') outfile.write("\tVAR = {};\n".format(VAR)) outfile.write("\tPAR = {};\n".format(PAR)) outfile.write("\tTT = {};\n".format(TT)) infile = open(SAGE_ROOT+'/src/sage/calculus/tides/seriesFile01.txt') for i in infile: outfile.write(i) infile.close() outfile.writelines(["\t\t"+i+"\n" for i in code]) infile = open(SAGE_ROOT+'/src/sage/calculus/tides/seriesFile02.txt') for i in infile: outfile.write(i) outfile.close() fname = tempdir + '/driver.c' fileoutput = tempdir + '/output' shutil.copy(SAGE_ROOT+'/src/sage/calculus/tides/driverFile00.txt', fname) outfile = open(fname, 'a') outfile.write('\n\tVARS = {} ;\n'.format(nvars-1)) outfile.write('\tPARS = 1;\n') outfile.write('\tdouble tolrel, tolabs, tini, tend, dt; \n') outfile.write('\tdouble v[VARS], p[PARS]; \n') for i in range(len(ics)): outfile.write('\tv[{}] = {} ; \n'.format(i, ics[i])) outfile.write('\ttini = {} ;\n'.format(initial)) outfile.write('\ttend = {} ;\n'.format(final)) outfile.write('\tdt = {} ;\n'.format(delta)) outfile.write('\ttolrel = {} ;\n'.format(tolrel)) outfile.write('\ttolabs = {} ;\n'.format(tolabs)) outfile.write('\textern char ofname[500];') outfile.write('\tstrcpy(ofname, "'+ fileoutput +'");\n') outfile.write('\tminc_tides(v,VARS,p,PARS,tini,tend,dt,tolrel,tolabs);\n') outfile.write('\treturn 0; \n }') outfile.close() runmefile = tempdir + '/runme' shutil.copy(SAGE_ROOT+'/src/sage/calculus/tides/minc_tides.c', tempdir) shutil.copy(SAGE_ROOT+'/src/sage/calculus/tides/minc_tides.h', tempdir) os.system('gcc -o ' + runmefile + ' ' + tempdir + '/*.c -lm -O2') os.system(tempdir+'/runme ') outfile = open(fileoutput) res = outfile.readlines() outfile.close() for i in range(len(res)): l=res[i] l = l.split(' ') l = filter(lambda a: len(a) > 2, l) res[i] = map(RR,l) #shutil.rmtree(tempdir) return res
def convert_list(f): """ - f is a vectorial function f(x1, x2, ...) -> [...] Create two lists: - The first list contains the subexpressions that appear in the construction of the function - The second one contains the corresponding operation used to create each subexpression """ variables = f[0].arguments() lis = flatten([fast_callable(i,vars=variables).op_list() for i in f], max_level=1) deflist = [] stack = [] const =[] stackcomp=[] detail=[] for i in lis: if i[0] == 'load_arg': stack.append(variables[i[1]]) elif i[0] == 'ipow': if i[1] in NN: basis = stack[-1] for j in range(i[1]-1): a=stack.pop(-1) detail.append(('mul', a, basis)) stack.append(a*basis) stackcomp.append(stack[-1]) else: detail.append(('pow',stack[-1],i[1])) stack[-1]=stack[-1]**i[1] stackcomp.append(stack[-1]) elif i[0] == 'load_const': const.append(i[1]) stack.append(i[1]) elif i == 'mul': a=stack.pop(-1) b=stack.pop(-1) detail.append(('mul', a, b)) stack.append(a*b) stackcomp.append(stack[-1]) elif i == 'div': a=stack.pop(-1) b=stack.pop(-1) detail.append(('div', a, b)) stack.append(b/a) stackcomp.append(stack[-1]) elif i == 'add': a=stack.pop(-1) b=stack.pop(-1) detail.append(('add',a,b)) stack.append(a+b) stackcomp.append(stack[-1]) elif i == 'pow': a=stack.pop(-1) b=stack.pop(-1) detail.append(('pow', b, a)) stack.append(b**a) stackcomp.append(stack[-1]) elif i[0] == 'py_call' and str(i[1])=='log': a=stack.pop(-1) detail.append(('log', a)) stack.append(log(a)) stackcomp.append(stack[-1]) elif i[0] == 'py_call' and str(i[1])=='exp': a=stack.pop(-1) detail.append(('exp', a)) stack.append(exp(a)) stackcomp.append(stack[-1]) elif i[0] == 'py_call' and str(i[1])=='sin': a=stack.pop(-1) detail.append(('sin', a)) detail.append(('cos', a)) stackcomp.append(sin(a)) stackcomp.append(cos(a)) stack.append(sin(a)) elif i[0] == 'py_call' and str(i[1])=='cos': a=stack.pop(-1) detail.append(('sin', a)) detail.append(('cos', a)) stackcomp.append(sin(a)) stackcomp.append(cos(a)) stack.append(cos(a)) elif i == 'neg': a = stack.pop(-1) detail.append(('mul', -1, a)) stack.append(-a) stackcomp.append(-a) return stackcomp,detail
def function_builder(eq): print("Preparing 4x{}x{} symbolic variables".format(nr, nt)) p = np.array([[SR.var("p_{}_{}".format(i, j)) for i in range(nr)] for j in range(nt)], dtype=object) wr = np.array([[SR.var("wr_{}_{}".format(i, j)) for i in range(nr)] for j in range(nt)], dtype=object) wt = np.array([[SR.var("wt_{}_{}".format(i, j)) for i in range(nr)] for j in range(nt)], dtype=object) wp = np.array([[SR.var("wp_{}_{}".format(i, j)) for i in range(nr)] for j in range(nt)], dtype=object) fun = np.array([SR(0)] * (4 * nr * nt), dtype=object) print("Discretizing equations") for i in range(1, nr - 1): for j in range(1, nt - 1): scheme = inner_scheme(p, wr, wt, wp, i, j) fun[i * nt + j] = eq[0].subs(scheme) fun[400 + i * nt + j] = eq[1].subs(scheme) fun[800 + i * nt + j] = eq[2].subs(scheme) fun[1200 + i * nt + j] = eq[3].subs(scheme) for i in range(1, nt - 1): scheme = border_scheme_inner(p, wr, wt, wp, i, j) fun[i] = eq[4].subs(scheme) fun[400 + i] = eq[5].subs(scheme) fun[800 + i] = eq[6].subs(scheme) fun[1200 + i] = eq[7].subs(scheme) for i in range(1, nt - 1): scheme = border_scheme_outer(p, wr, wt, wp, i, j) fun[380 + i] = eq[8].subs(scheme) fun[780 + i] = eq[9].subs(scheme) fun[1180 + i] = eq[10].subs(scheme) fun[1580 + i] = eq[11].subs(scheme) for i in range(1, nr - 1): scheme = border_scheme_up(p, wr, wt, wp, i, j) fun[i * nt] = eq[12].subs(scheme) fun[400 + i * nt] = eq[13].subs(scheme) fun[800 + i * nt] = eq[14].subs(scheme) fun[1200 + i * nt] = eq[15].subs(scheme) for i in range(1, nr - 1): scheme = border_scheme_down(p, wr, wt, wp, i, j) fun[19 + i * nt] = eq[12].subs(scheme) fun[419 + i * nt] = eq[13].subs(scheme) fun[819 + i * nt] = eq[14].subs(scheme) fun[1219 + i * nt] = eq[15].subs(scheme) # fun[0] = eq[8].subs(scheme) # fun[19] = eq[8].subs(scheme) # fun[399] = eq[8].subs(scheme) # fun[380] = eq[8.subs(scheme)] puis increments de 400 for i in range(4 * nr * nt): fun[i] = fun[i].subs({x: (i + 1) * dr, y: j * dt}) var = np.concatenate( (p.flatten(), wr.flatten(), wt.flatten(), wp.flatten())) print("Transforming function to fast callable") for i in range(4 * nr * nt): fun[i] = fast_callable(fun[i], vars=tuple(var), domain=float) return lambda x: map(methodcaller('__call__', *x), fun)