Skip to content

Commit c12ccbc

Browse files
Update wronskian_second_order_de.py
1 parent 99753a2 commit c12ccbc

File tree

1 file changed

+100
-140
lines changed

1 file changed

+100
-140
lines changed

maths/wronskian_second_order_de.py

Lines changed: 100 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,180 +1,140 @@
11
"""
2-
Module to analyze homogeneous linear differential equations.
2+
wronskian_second_order_de.py
3+
A symbolic and numerical exploration of the Wronskian for second-order linear differential equations.
34
4-
It supports:
5-
- Second-order equations: a*y'' + b*y' + c*y = 0
6-
- First-order equations: b*y' + c*y = 0 (when a = 0)
5+
This program:
6+
1. Takes coefficients (a, b, c) for a*y'' + b*y' + c*y = 0.
7+
2. Computes characteristic roots.
8+
3. Classifies the solution type.
9+
4. Constructs the general solution.
10+
5. Demonstrates Wronskian computation.
711
8-
Features:
9-
- Computes characteristic roots (for second-order)
10-
- Derives fundamental solutions
11-
- Calculates first derivatives
12-
- Evaluates the Wronskian determinant
13-
- Tests for linear independence
14-
15-
References:
16-
https://en.wikipedia.org/wiki/Linear_differential_equation
12+
Author: Venkat Thadi
1713
"""
1814

15+
import math
1916
import cmath
20-
from sympy import symbols, exp, cos, sin, diff, simplify
21-
22-
23-
def compute_characteristic_roots(
24-
coefficient_a: float, coefficient_b: float, coefficient_c: float
25-
) -> tuple[complex, complex]:
26-
"""
27-
Compute roots of the characteristic equation:
28-
a*r^2 + b*r + c = 0
2917

30-
>>> compute_characteristic_roots(1, -2, 1)
31-
((1+0j), (1+0j))
32-
>>> compute_characteristic_roots(1, 0, 1)
33-
((0+1j), (0-1j))
18+
def compute_characteristic_roots(a: float, b: float, c: float) -> tuple[complex, complex]:
3419
"""
35-
discriminant = coefficient_b**2 - 4 * coefficient_a * coefficient_c
36-
sqrt_discriminant = cmath.sqrt(discriminant)
37-
root_1 = (-coefficient_b + sqrt_discriminant) / (2 * coefficient_a)
38-
root_2 = (-coefficient_b - sqrt_discriminant) / (2 * coefficient_a)
39-
return root_1, root_2
20+
Compute characteristic roots for a second-order homogeneous linear DE.
4021
41-
42-
def construct_fundamental_solutions(root_1: complex, root_2: complex):
43-
"""
44-
Construct fundamental solutions (y1, y2) of a 2nd-order ODE.
45-
46-
>>> from sympy import symbols, exp
47-
>>> x = symbols('x')
48-
>>> r1, r2 = 1, 1
49-
>>> construct_fundamental_solutions(r1, r2)
50-
(exp(x), x*exp(x))
22+
>>> compute_characteristic_roots(1, -3, 2)
23+
(2.0, 1.0)
24+
>>> compute_characteristic_roots(1, 2, 5)
25+
((-1+2j), (-1-2j))
5126
"""
52-
variable_x = symbols("x")
27+
if a == 0:
28+
raise ValueError("Coefficient 'a' cannot be zero for a second-order equation.")
5329

54-
# Case 1: Real and equal roots
55-
if root_1 == root_2 and root_1.imag == 0:
56-
solution_1 = exp(root_1.real * variable_x)
57-
solution_2 = variable_x * exp(root_1.real * variable_x)
30+
discriminant = b ** 2 - 4 * a * c
31+
sqrt_disc = cmath.sqrt(discriminant)
32+
root1 = (-b + sqrt_disc) / (2 * a)
33+
root2 = (-b - sqrt_disc) / (2 * a)
5834

59-
# Case 2: Real and distinct roots
60-
elif root_1.imag == 0 and root_2.imag == 0:
61-
solution_1 = exp(root_1.real * variable_x)
62-
solution_2 = exp(root_2.real * variable_x)
35+
# Simplify if roots are purely real
36+
if abs(root1.imag) < 1e-12:
37+
root1 = float(root1.real)
38+
if abs(root2.imag) < 1e-12:
39+
root2 = float(root2.real)
6340

64-
# Case 3: Complex conjugate roots (α ± iβ)
65-
else:
66-
alpha = root_1.real
67-
beta = abs(root_1.imag)
68-
solution_1 = exp(alpha * variable_x) * cos(beta * variable_x)
69-
solution_2 = exp(alpha * variable_x) * sin(beta * variable_x)
41+
return root1, root2
7042

71-
return solution_1, solution_2
7243

73-
74-
def compute_wronskian(function_1, function_2):
44+
def classify_solution_type(root1: complex, root2: complex) -> str:
7545
"""
76-
Compute the Wronskian determinant of two functions.
77-
78-
>>> from sympy import symbols, exp
79-
>>> x = symbols('x')
80-
>>> f1, f2 = exp(x), x*exp(x)
81-
>>> compute_wronskian(f1, f2)
82-
exp(2*x)
46+
Classify the nature of the roots.
47+
48+
>>> classify_solution_type(2, 1)
49+
'Distinct Real Roots'
50+
>>> classify_solution_type(1+2j, 1-2j)
51+
'Complex Conjugate Roots'
52+
>>> classify_solution_type(3, 3)
53+
'Repeated Real Roots'
8354
"""
84-
variable_x = symbols("x")
85-
derivative_1 = diff(function_1, variable_x)
86-
derivative_2 = diff(function_2, variable_x)
87-
wronskian = simplify(function_1 * derivative_2 - function_2 * derivative_1)
88-
return wronskian
55+
if isinstance(root1, complex) and isinstance(root2, complex) and root1.imag != 0:
56+
return "Complex Conjugate Roots"
57+
elif root1 == root2:
58+
return "Repeated Real Roots"
59+
else:
60+
return "Distinct Real Roots"
8961

9062

91-
def solve_first_order_equation(coefficient_b: float, coefficient_c: float) -> None:
63+
def compute_wronskian(f, g, f_prime, g_prime, x: float) -> float:
9264
"""
93-
Solve the first-order ODE: b*y' + c*y = 0
94-
and display its general solution and Wronskian.
95-
96-
>>> solve_first_order_equation(2, 4)
65+
Compute Wronskian determinant W(f, g) = f * g' - f' * g.
66+
67+
>>> import math
68+
>>> def f(x): return math.exp(x)
69+
>>> def g(x): return x * math.exp(x)
70+
>>> def f_prime(x): return math.exp(x)
71+
>>> def g_prime(x): return math.exp(x) + x * math.exp(x)
72+
>>> round(compute_wronskian(f, g, f_prime, g_prime, 0), 3)
73+
1.0
9774
"""
98-
variable_x = symbols("x")
99-
if coefficient_b == 0:
100-
print("Error: Both a and b cannot be zero. Not a valid differential equation.")
101-
return
102-
103-
# Simplified form: y' + (c/b)*y = 0
104-
constant_k = coefficient_c / coefficient_b
105-
solution = exp(-constant_k * variable_x)
106-
107-
derivative_solution = diff(solution, variable_x)
108-
wronskian = simplify(solution * derivative_solution)
75+
return f(x) * g_prime(x) - f_prime(x) * g(x)
10976

110-
print("\n--- First-Order Differential Equation ---")
111-
print(f"Equation: {coefficient_b}*y' + {coefficient_c}*y = 0")
112-
print(f"Solution: y = C * e^(-({coefficient_c}/{coefficient_b}) * x)")
113-
print(f"y'(x) = {derivative_solution}")
114-
print(f"Wronskian (single function): {wronskian}")
115-
print("Linear independence: Trivial (only one solution).")
11677

117-
118-
def analyze_differential_equation(
119-
coefficient_a: float, coefficient_b: float, coefficient_c: float
120-
) -> None:
78+
def construct_general_solution(root1: complex, root2: complex) -> str:
12179
"""
122-
Determine the type of equation and analyze it accordingly.
80+
Construct the general solution based on the roots.
81+
82+
>>> construct_general_solution(2, 1)
83+
'y(x) = C1 * e^(2x) + C2 * e^(1x)'
84+
>>> construct_general_solution(3, 3)
85+
'y(x) = (C1 + C2 * x) * e^(3x)'
86+
>>> construct_general_solution(-1+2j, -1-2j)
87+
'y(x) = e^(-1x) * (C1 * cos(2x) + C2 * sin(2x))'
12388
"""
124-
# Case 1: Not a valid DE
125-
if coefficient_a == 0 and coefficient_b == 0:
126-
print(
127-
"Error: Both 'a' and 'b' cannot be zero. Not a valid differential equation."
128-
)
129-
return
130-
131-
# Case 2: First-order DE
132-
if coefficient_a == 0:
133-
solve_first_order_equation(coefficient_b, coefficient_c)
134-
return
135-
136-
# Case 3: Second-order DE
137-
print("\n--- Second-Order Differential Equation ---")
138-
root_1, root_2 = compute_characteristic_roots(
139-
coefficient_a, coefficient_b, coefficient_c
140-
)
141-
142-
print(f"Characteristic roots: r1 = {root_1}, r2 = {root_2}")
89+
if isinstance(root1, complex) and root1.imag != 0:
90+
alpha = round(root1.real, 10)
91+
beta = round(abs(root1.imag), 10)
92+
return f"y(x) = e^({alpha:g}x) * (C1 * cos({beta:g}x) + C2 * sin({beta:g}x))"
93+
elif root1 == root2:
94+
return f"y(x) = (C1 + C2 * x) * e^({root1:g}x)"
95+
else:
96+
return f"y(x) = C1 * e^({root1:g}x) + C2 * e^({root2:g}x)"
14397

144-
function_1, function_2 = construct_fundamental_solutions(root_1, root_2)
14598

146-
variable_x = symbols("x")
147-
derivative_1 = diff(function_1, variable_x)
148-
derivative_2 = diff(function_2, variable_x)
149-
wronskian = compute_wronskian(function_1, function_2)
99+
def analyze_differential_equation(a: float, b: float, c: float) -> None:
100+
"""
101+
Analyze the DE and print the roots, type, and general solution.
150102
151-
print(f"y₁(x) = {function_1}")
152-
print(f"y₂(x) = {function_2}")
153-
print(f"y₁'(x) = {derivative_1}")
154-
print(f"y₂'(x) = {derivative_2}")
155-
print(f"Wronskian: {wronskian}")
103+
>>> analyze_differential_equation(1, -3, 2) # doctest: +ELLIPSIS
104+
Characteristic Roots: (2.0, 1.0)
105+
Solution Type: Distinct Real Roots
106+
General Solution: y(x) = C1 * e^(2x) + C2 * e^(1x)
107+
"""
108+
roots = compute_characteristic_roots(a, b, c)
109+
root1, root2 = roots
110+
sol_type = classify_solution_type(root1, root2)
111+
general_solution = construct_general_solution(root1, root2)
156112

157-
if wronskian == 0:
158-
print("The functions are linearly dependent.")
159-
else:
160-
print("The functions are linearly independent.")
113+
print(f"Characteristic Roots: ({root1:.1f}, {root2:.1f})")
114+
print(f"Solution Type: {sol_type}")
115+
print(f"General Solution: {general_solution}")
161116

162117

163118
def main() -> None:
164119
"""
165-
Entry point of the program.
120+
Main function to run the second-order differential equation Wronskian analysis.
121+
122+
Interactive input is expected, so this function is skipped in doctests.
166123
"""
167124
print("Enter coefficients for the equation a*y'' + b*y' + c*y = 0")
168-
try:
169-
coefficient_a = float(input("a = ").strip())
170-
coefficient_b = float(input("b = ").strip())
171-
coefficient_c = float(input("c = ").strip())
172-
except ValueError:
173-
print("Invalid input. Please enter numeric values for coefficients.")
125+
126+
# Skipping main in doctests because input() cannot be tested directly
127+
a = float(input("a = ").strip()) # doctest: +SKIP
128+
b = float(input("b = ").strip()) # doctest: +SKIP
129+
c = float(input("c = ").strip()) # doctest: +SKIP
130+
131+
if a == 0:
132+
print("Invalid input: coefficient 'a' cannot be zero.")
174133
return
175134

176-
analyze_differential_equation(coefficient_a, coefficient_b, coefficient_c)
135+
analyze_differential_equation(a, b, c)
136+
177137

178138

179139
if __name__ == "__main__":
180-
main()
140+
main() # doctest: +SKIP

0 commit comments

Comments
 (0)