Coverage for /usr/lib/python3/dist-packages/sympy/plotting/textplot.py: 10%

87 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1from sympy.core.numbers import Float 

2from sympy.core.symbol import Dummy 

3from sympy.utilities.lambdify import lambdify 

4 

5import math 

6 

7 

8def is_valid(x): 

9 """Check if a floating point number is valid""" 

10 if x is None: 

11 return False 

12 if isinstance(x, complex): 

13 return False 

14 return not math.isinf(x) and not math.isnan(x) 

15 

16 

17def rescale(y, W, H, mi, ma): 

18 """Rescale the given array `y` to fit into the integer values 

19 between `0` and `H-1` for the values between ``mi`` and ``ma``. 

20 """ 

21 y_new = [] 

22 

23 norm = ma - mi 

24 offset = (ma + mi) / 2 

25 

26 for x in range(W): 

27 if is_valid(y[x]): 

28 normalized = (y[x] - offset) / norm 

29 if not is_valid(normalized): 

30 y_new.append(None) 

31 else: 

32 rescaled = Float((normalized*H + H/2) * (H-1)/H).round() 

33 rescaled = int(rescaled) 

34 y_new.append(rescaled) 

35 else: 

36 y_new.append(None) 

37 return y_new 

38 

39 

40def linspace(start, stop, num): 

41 return [start + (stop - start) * x / (num-1) for x in range(num)] 

42 

43 

44def textplot_str(expr, a, b, W=55, H=21): 

45 """Generator for the lines of the plot""" 

46 free = expr.free_symbols 

47 if len(free) > 1: 

48 raise ValueError( 

49 "The expression must have a single variable. (Got {})" 

50 .format(free)) 

51 x = free.pop() if free else Dummy() 

52 f = lambdify([x], expr) 

53 a = float(a) 

54 b = float(b) 

55 

56 # Calculate function values 

57 x = linspace(a, b, W) 

58 y = [] 

59 for val in x: 

60 try: 

61 y.append(f(val)) 

62 # Not sure what exceptions to catch here or why... 

63 except (ValueError, TypeError, ZeroDivisionError): 

64 y.append(None) 

65 

66 # Normalize height to screen space 

67 y_valid = list(filter(is_valid, y)) 

68 if y_valid: 

69 ma = max(y_valid) 

70 mi = min(y_valid) 

71 if ma == mi: 

72 if ma: 

73 mi, ma = sorted([0, 2*ma]) 

74 else: 

75 mi, ma = -1, 1 

76 else: 

77 mi, ma = -1, 1 

78 y_range = ma - mi 

79 precision = math.floor(math.log(y_range, 10)) - 1 

80 precision *= -1 

81 mi = round(mi, precision) 

82 ma = round(ma, precision) 

83 y = rescale(y, W, H, mi, ma) 

84 

85 y_bins = linspace(mi, ma, H) 

86 

87 # Draw plot 

88 margin = 7 

89 for h in range(H - 1, -1, -1): 

90 s = [' '] * W 

91 for i in range(W): 

92 if y[i] == h: 

93 if (i == 0 or y[i - 1] == h - 1) and (i == W - 1 or y[i + 1] == h + 1): 

94 s[i] = '/' 

95 elif (i == 0 or y[i - 1] == h + 1) and (i == W - 1 or y[i + 1] == h - 1): 

96 s[i] = '\\' 

97 else: 

98 s[i] = '.' 

99 

100 if h == 0: 

101 for i in range(W): 

102 s[i] = '_' 

103 

104 # Print y values 

105 if h in (0, H//2, H - 1): 

106 prefix = ("%g" % y_bins[h]).rjust(margin)[:margin] 

107 else: 

108 prefix = " "*margin 

109 s = "".join(s) 

110 if h == H//2: 

111 s = s.replace(" ", "-") 

112 yield prefix + " |" + s 

113 

114 # Print x values 

115 bottom = " " * (margin + 2) 

116 bottom += ("%g" % x[0]).ljust(W//2) 

117 if W % 2 == 1: 

118 bottom += ("%g" % x[W//2]).ljust(W//2) 

119 else: 

120 bottom += ("%g" % x[W//2]).ljust(W//2-1) 

121 bottom += "%g" % x[-1] 

122 yield bottom 

123 

124 

125def textplot(expr, a, b, W=55, H=21): 

126 r""" 

127 Print a crude ASCII art plot of the SymPy expression 'expr' (which 

128 should contain a single symbol, e.g. x or something else) over the 

129 interval [a, b]. 

130 

131 Examples 

132 ======== 

133 

134 >>> from sympy import Symbol, sin 

135 >>> from sympy.plotting import textplot 

136 >>> t = Symbol('t') 

137 >>> textplot(sin(t)*t, 0, 15) 

138 14 | ... 

139 | . 

140 | . 

141 | . 

142 | . 

143 | ... 

144 | / . . 

145 | / 

146 | / . 

147 | . . . 

148 1.5 |----.......-------------------------------------------- 

149 |.... \ . . 

150 | \ / . 

151 | .. / . 

152 | \ / . 

153 | .... 

154 | . 

155 | . . 

156 | 

157 | . . 

158 -11 |_______________________________________________________ 

159 0 7.5 15 

160 """ 

161 for line in textplot_str(expr, a, b, W, H): 

162 print(line)