summaryrefslogtreecommitdiff
path: root/tutorials/module_1/errorPlots.py
blob: 8351fc8f63f7bbf1d0b0bb52bbcba91f8ed90f9a (plain)
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
# Import required packages
import numpy as np
import matplotlib.pyplot as plt

# ---------------------------------------------------------------------------
# PART 1: Visualizing Truncation Error and Approximate Integral
# ---------------------------------------------------------------------------

# Generate true curve: y = sin(x)
x_fine = np.linspace(0, np.pi/2, 400)
y_fine = np.sin(x_fine)

# Linear approximation (straight line between 0 and pi/2)
x_line = np.array([0, np.pi/2])
y_line = np.sin(x_line)

# Plot true curve and linear approximation
plt.figure(figsize=(8, 6))

plt.plot(x_fine, y_fine, label='True function: $y = \sin(x)$', color='black', linewidth=3)
plt.plot(x_line, y_line, label='Linear approximation', color='red', linestyle='--', linewidth=2)

# Shade area between true curve and linear approximation (truncation error)
plt.fill_between(x_fine, y_fine, np.interp(x_fine, x_line, y_line),
                 where=(y_fine > np.interp(x_fine, x_line, y_line)),
                 interpolate=True, color='gray', alpha=0.5, label='Truncation Error')

# Shade approximate integral (area under linear approximation)
plt.fill_between(x_fine, 0, np.interp(x_fine, x_line, y_line),
                 color='lightblue', alpha=0.5, label='Approximate Integral')

# Remove ticks
plt.xticks([])
plt.yticks([])
plt.grid(False)

# Labels and title
plt.xlabel('x', fontsize=20)
plt.ylabel('y', fontsize=20)
plt.title('Truncation Error', fontsize=22)
plt.legend(fontsize=16, loc='lower right')
plt.tight_layout()
plt.show()

# ---------------------------------------------------------------------------
# PART 2: Error Plot for Truncation, Round-off, and Total Errors
# ---------------------------------------------------------------------------

# Set up for log-log error plot
h = np.logspace(-16, -1, 400)
truncation_error = 1e-1 * h
roundoff_error = 1e-16 / h
total_error = truncation_error + roundoff_error

# Find minimum total error point
min_idx = np.argmin(total_error)
min_h = h[min_idx]
arrow_target_h = 2.5 * min_h
arrow_target_err = total_error[np.argmin(np.abs(h - arrow_target_h))]

# Plot errors
plt.figure(figsize=(10, 7))

plt.loglog(h, truncation_error, label='Truncation error', linewidth=3, color='blue')
plt.loglog(h, roundoff_error, label='Round-off error', linewidth=3, color='orange')
plt.loglog(h, total_error, label='Total error', linewidth=4, color='black')

# Annotation
plt.annotate('Point of\n diminishing\n returns',
             xy=(arrow_target_h, arrow_target_err), xycoords='data',
             xytext=(arrow_target_h, 15 * arrow_target_err), textcoords='data',
             arrowprops=dict(arrowstyle='->', lw=2),
             fontsize=20, ha='center')

# Remove ticks and grid
plt.xticks([])
plt.yticks([])
plt.grid(False)

# Labels and legend
plt.xlabel('Log step size', fontsize=22)
plt.ylabel('Log error', fontsize=22)
plt.legend(fontsize=20, loc='upper right')
plt.tight_layout()
plt.show()


# ---------------------------------------------------------------------------
# PART 3: Error Plot for Truncation with more steps
# ---------------------------------------------------------------------------

# Fix legend duplication and improve clarity
import numpy as np
import matplotlib.pyplot as plt

# Generate true curve: y = sin(x) over 0 to π
x_fine = np.linspace(0, np.pi, 800)
y_fine = np.sin(x_fine)

# Choose multiple linear segments
n_steps = 3  # number of straight line segments
x_steps = np.linspace(0, np.pi, n_steps + 1)
y_steps = np.sin(x_steps)

# Plot true curve
plt.figure(figsize=(8, 6))
plt.plot(x_fine, y_fine, label='True function: $y = \\sin(x)$', color='black', linewidth=3)

# Plot piecewise linear approximation
plt.plot(x_steps, y_steps, label='Linear approximation', color='red', linestyle='--', linewidth=2)

# Track whether we've added the truncation label already
truncation_label_added = False

# Shade truncation error (avoid duplicate legend entry)
for i in range(n_steps):
    x_segment = np.linspace(x_steps[i], x_steps[i+1], 100)
    y_segment_line = np.interp(x_segment, [x_steps[i], x_steps[i+1]], [y_steps[i], y_steps[i+1]])
    y_segment_true = np.sin(x_segment)

    label = 'Truncation error' if not truncation_label_added else None
    plt.fill_between(x_segment, y_segment_true, y_segment_line,
                     where=(y_segment_true > y_segment_line),
                     interpolate=True, color='gray', alpha=0.5, label=label)
    truncation_label_added = True

# Shade approximate integral under the piecewise linear approximation
plt.fill_between(x_fine, 0, np.interp(x_fine, x_steps, y_steps),
                 color='lightblue', alpha=0.5, label='Approximate integral')

# Remove ticks
plt.xticks([])
plt.yticks([])
plt.grid(False)

# Labels and title
plt.xlabel('x', fontsize=20)
plt.ylabel('y', fontsize=20)
plt.title('Truncation Error', fontsize=24)
plt.legend(fontsize=16, loc='lower right')
plt.tight_layout()
plt.show()