Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

"""Utility functions for classifying and solving 

ordinary and partial differential equations. 

 

Contains 

======== 

_preprocess 

ode_order 

_desolve 

 

""" 

from __future__ import print_function, division 

 

from sympy.core.function import Derivative, AppliedUndef 

from sympy.core.relational import Equality 

from sympy.core.symbol import Wild 

 

def _preprocess(expr, func=None, hint='_Integral'): 

    """Prepare expr for solving by making sure that differentiation 

    is done so that only func remains in unevaluated derivatives and 

    (if hint doesn't end with _Integral) that doit is applied to all 

    other derivatives. If hint is None, don't do any differentiation. 

    (Currently this may cause some simple differential equations to 

    fail.) 

 

    In case func is None, an attempt will be made to autodetect the 

    function to be solved for. 

 

    >>> from sympy.solvers.deutils import _preprocess 

    >>> from sympy import Derivative, Function, Integral, sin 

    >>> from sympy.abc import x, y, z 

    >>> f, g = map(Function, 'fg') 

 

    Apply doit to derivatives that contain more than the function 

    of interest: 

 

    >>> _preprocess(Derivative(f(x) + x, x)) 

    (Derivative(f(x), x) + 1, f(x)) 

 

    Do others if the differentiation variable(s) intersect with those 

    of the function of interest or contain the function of interest: 

 

    >>> _preprocess(Derivative(g(x), y, z), f(y)) 

    (0, f(y)) 

    >>> _preprocess(Derivative(f(y), z), f(y)) 

    (0, f(y)) 

 

    Do others if the hint doesn't end in '_Integral' (the default 

    assumes that it does): 

 

    >>> _preprocess(Derivative(g(x), y), f(x)) 

    (Derivative(g(x), y), f(x)) 

    >>> _preprocess(Derivative(f(x), y), f(x), hint='') 

    (0, f(x)) 

 

    Don't do any derivatives if hint is None: 

 

    >>> eq = Derivative(f(x) + 1, x) + Derivative(f(x), y) 

    >>> _preprocess(eq, f(x), hint=None) 

    (Derivative(f(x) + 1, x) + Derivative(f(x), y), f(x)) 

 

    If it's not clear what the function of interest is, it must be given: 

 

    >>> eq = Derivative(f(x) + g(x), x) 

    >>> _preprocess(eq, g(x)) 

    (Derivative(f(x), x) + Derivative(g(x), x), g(x)) 

    >>> try: _preprocess(eq) 

    ... except ValueError: print("A ValueError was raised.") 

    A ValueError was raised. 

 

    """ 

 

    derivs = expr.atoms(Derivative) 

    if not func: 

        funcs = set().union(*[d.atoms(AppliedUndef) for d in derivs]) 

        if len(funcs) != 1: 

            raise ValueError('The function cannot be ' 

                'automatically detected for %s.' % expr) 

        func = funcs.pop() 

    fvars = set(func.args) 

    if hint is None: 

        return expr, func 

    reps = [(d, d.doit()) for d in derivs if not hint.endswith('_Integral') or 

            d.has(func) or set(d.variables) & fvars] 

    eq = expr.subs(reps) 

    return eq, func 

 

def ode_order(expr, func): 

    """ 

    Returns the order of a given differential 

    equation with respect to func. 

 

    This function is implemented recursively. 

 

    Examples 

    ======== 

 

    >>> from sympy import Function 

    >>> from sympy.solvers.deutils import ode_order 

    >>> from sympy.abc import x 

    >>> f, g = map(Function, ['f', 'g']) 

    >>> ode_order(f(x).diff(x, 2) + f(x).diff(x)**2 + 

    ... f(x).diff(x), f(x)) 

    2 

    >>> ode_order(f(x).diff(x, 2) + g(x).diff(x, 3), f(x)) 

    2 

    >>> ode_order(f(x).diff(x, 2) + g(x).diff(x, 3), g(x)) 

    3 

 

    """ 

    a = Wild('a', exclude=[func]) 

    if expr.match(a): 

        return 0 

 

    if isinstance(expr, Derivative): 

        if expr.args[0] == func: 

            return len(expr.variables) 

        else: 

            order = 0 

            for arg in expr.args[0].args: 

                order = max(order, ode_order(arg, func) + len(expr.variables)) 

            return order 

    else: 

        order = 0 

        for arg in expr.args: 

            order = max(order, ode_order(arg, func)) 

        return order 

 

def _desolve(eq, func=None, hint="default", ics=None, simplify=True, **kwargs): 

    """This is a helper function to dsolve and pdsolve in the ode 

    and pde modules. 

 

    If the hint provided to the function is "default", then a dict with 

    the following keys are returned 

 

    'func'    - It provides the function for which the differential equation 

                has to be solved. This is useful when the expression has 

                more than one function in it. 

 

    'default' - The default key as returned by classifier functions in ode 

                and pde.py 

 

    'hint'    - The hint given by the user for which the differential equation 

                is to be solved. If the hint given by the user is 'default', 

                then the value of 'hint' and 'default' is the same. 

 

    'order'   - The order of the function as returned by ode_order 

 

    'match'   - It returns the match as given by the classifier functions, for 

                the default hint. 

 

    If the hint provided to the function is not "default" and is not in 

    ('all', 'all_Integral', 'best'), then a dict with the above mentioned keys 

    is returned along with the keys which are returned when dict in 

    classify_ode or classify_pde is set True 

 

    If the hint given is in ('all', 'all_Integral', 'best'), then this function 

    returns a nested dict, with the keys, being the set of classified hints 

    returned by classifier functions, and the values being the dict of form 

    as mentioned above. 

 

    Key 'eq' is a common key to all the above mentioned hints which returns an 

    expression if eq given by user is an Equality. 

 

    See Also 

    ======== 

    classify_ode(ode.py) 

    classify_pde(pde.py) 

    """ 

    prep = kwargs.pop('prep', True) 

    if isinstance(eq, Equality): 

        eq = eq.lhs - eq.rhs 

 

    # preprocess the equation and find func if not given 

    if prep or func is None: 

        eq, func = _preprocess(eq, func) 

        prep = False 

 

    # type is an argument passed by the solve functions in ode and pde.py 

    # that identifies whether the function caller is an ordinary 

    # or partial differential equation. Accordingly corresponding 

    # changes are made in the function. 

    type = kwargs.get('type', None) 

    xi = kwargs.get('xi') 

    eta = kwargs.get('eta') 

    x0 = kwargs.get('x0', 0) 

    terms = kwargs.get('n') 

 

    if type == 'ode': 

        from sympy.solvers.ode import classify_ode, allhints 

        classifier = classify_ode 

        string = 'ODE ' 

        dummy = '' 

 

    elif type == 'pde': 

        from sympy.solvers.pde import classify_pde, allhints 

        classifier = classify_pde 

        string = 'PDE ' 

        dummy = 'p' 

 

    # Magic that should only be used internally.  Prevents classify_ode from 

    # being called more than it needs to be by passing its results through 

    # recursive calls. 

    if kwargs.get('classify', True): 

        hints = classifier(eq, func, dict=True, ics=ics, xi=xi, eta=eta, 

        n=terms, x0=x0, prep=prep) 

 

    else: 

        # Here is what all this means: 

        # 

        # hint:    The hint method given to _desolve() by the user. 

        # hints:   The dictionary of hints that match the DE, along with other 

        #          information (including the internal pass-through magic). 

        # default: The default hint to return, the first hint from allhints 

        #          that matches the hint; obtained from classify_ode(). 

        # match:   Dictionary containing the match dictionary for each hint 

        #          (the parts of the DE for solving).  When going through the 

        #          hints in "all", this holds the match string for the current 

        #          hint. 

        # order:   The order of the DE, as determined by ode_order(). 

        hints = kwargs.get('hint', 

                           {'default': hint, 

                            hint: kwargs['match'], 

                            'order': kwargs['order']}) 

    if hints['order'] == 0: 

        raise ValueError( 

            str(eq) + " is not a differential equation in " + str(func)) 

 

    if not hints['default']: 

        # classify_ode will set hints['default'] to None if no hints match. 

        if hint not in allhints and hint != 'default': 

            raise ValueError("Hint not recognized: " + hint) 

        elif hint not in hints['ordered_hints'] and hint != 'default': 

            raise ValueError(string + str(eq) + " does not match hint " + hint) 

        else: 

            raise NotImplementedError(dummy + "solve" + ": Cannot solve " + str(eq)) 

    if hint == 'default': 

        return _desolve(eq, func, ics=ics, hint=hints['default'], simplify=simplify, 

                      prep=prep, x0=x0, classify=False, order=hints['order'], 

                      match=hints[hints['default']], xi=xi, eta=eta, n=terms, type=type) 

    elif hint in ('all', 'all_Integral', 'best'): 

        retdict = {} 

        failedhints = {} 

        gethints = set(hints) - set(['order', 'default', 'ordered_hints']) 

        if hint == 'all_Integral': 

            for i in hints: 

                if i.endswith('_Integral'): 

                    gethints.remove(i[:-len('_Integral')]) 

            # special cases 

            for k in ["1st_homogeneous_coeff_best", "1st_power_series", 

                "lie_group", "2nd_power_series_ordinary", "2nd_power_series_regular"]: 

                if k in gethints: 

                    gethints.remove(k) 

        for i in gethints: 

            sol = _desolve(eq, func, ics=ics, hint=i, x0=x0, simplify=simplify, prep=prep, 

                classify=False, n=terms, order=hints['order'], match=hints[i], type=type) 

            retdict[i] = sol 

        retdict['all'] = True 

        retdict['eq'] = eq 

        return retdict 

    elif hint not in allhints:  # and hint not in ('default', 'ordered_hints'): 

        raise ValueError("Hint not recognized: " + hint) 

    elif hint not in hints: 

        raise ValueError(string + str(eq) + " does not match hint " + hint) 

    else: 

        # Key added to identify the hint needed to solve the equation 

        hints['hint'] = hint 

    hints.update({'func': func, 'eq': eq}) 

    return hints