Skip to content
153 changes: 153 additions & 0 deletions maths/wronskian_second_order_de.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
"""
wronskian_second_order_de.py
A symbolic and numerical exploration of the Wronskian for second-order linear differential equations.

Check failure on line 3 in maths/wronskian_second_order_de.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

maths/wronskian_second_order_de.py:3:89: E501 Line too long (101 > 88)

This program:
1. Takes coefficients (a, b, c) for a*y'' + b*y' + c*y = 0.
2. Computes characteristic roots.
3. Classifies the solution type.
4. Constructs the general solution.
5. Demonstrates Wronskian computation.

Author: Venkat Thadi

References: https://tutorial.math.lamar.edu/classes/de/wronskian.aspx
"""

import cmath

Check failure on line 17 in maths/wronskian_second_order_de.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (I001)

maths/wronskian_second_order_de.py:17:1: I001 Import block is un-sorted or un-formatted

def compute_characteristic_roots(
a: float, b: float, c: float

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide descriptive name for the parameter: a

Please provide descriptive name for the parameter: b

Please provide descriptive name for the parameter: c

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide descriptive name for the parameter: a

Please provide descriptive name for the parameter: b

Please provide descriptive name for the parameter: c

) -> tuple[complex, complex]:
"""
Compute characteristic roots for a second-order homogeneous linear DE.
a, b, c -> coefficients

Check failure on line 25 in maths/wronskian_second_order_de.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W293)

maths/wronskian_second_order_de.py:25:1: W293 Blank line contains whitespace
>>> compute_characteristic_roots(1, -3, 2)
(2.0, 1.0)
>>> compute_characteristic_roots(1, 2, 5)
((-1+2j), (-1-2j))
"""
if a == 0:
raise ValueError("Coefficient 'a' cannot be zero for a second-order equation.")

discriminant = b**2 - 4 * a * c
sqrt_disc = cmath.sqrt(discriminant)
root1 = (-b + sqrt_disc) / (2 * a)
root2 = (-b - sqrt_disc) / (2 * a)

# Simplify if roots are purely real
if abs(root1.imag) < 1e-12:
root1 = float(root1.real)
if abs(root2.imag) < 1e-12:
root2 = float(root2.real)

return root1, root2


def classify_solution_type(root1: complex, root2: complex) -> str:
"""
Classify the nature of the roots.

>>> classify_solution_type(2, 1)
'Distinct Real Roots'
>>> classify_solution_type(1+2j, 1-2j)
'Complex Conjugate Roots'
>>> classify_solution_type(3, 3)
'Repeated Real Roots'
"""
if isinstance(root1, complex) and isinstance(root2, complex) and root1.imag != 0:
return "Complex Conjugate Roots"
elif root1 == root2:
return "Repeated Real Roots"
else:
return "Distinct Real Roots"


def compute_wronskian(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there is no test file in this pull request nor any test function or class in the file maths/wronskian_second_order_de.py, please provide doctest for the function compute_wronskian

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there is no test file in this pull request nor any test function or class in the file maths/wronskian_second_order_de.py, please provide doctest for the function compute_wronskian

function_1: Callable[[float], float],

Check failure on line 68 in maths/wronskian_second_order_de.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (F821)

maths/wronskian_second_order_de.py:68:17: F821 Undefined name `Callable`
function_2: Callable[[float], float],

Check failure on line 69 in maths/wronskian_second_order_de.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (F821)

maths/wronskian_second_order_de.py:69:17: F821 Undefined name `Callable`
derivative_1: Callable[[float], float],

Check failure on line 70 in maths/wronskian_second_order_de.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (F821)

maths/wronskian_second_order_de.py:70:19: F821 Undefined name `Callable`
derivative_2: Callable[[float], float],

Check failure on line 71 in maths/wronskian_second_order_de.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (F821)

maths/wronskian_second_order_de.py:71:19: F821 Undefined name `Callable`
evaluation_point: float
) -> float:
"""
Compute the Wronskian of two functions at a given point.

Parameters:
function_1 (Callable[[float], float]): The first function f(x).
function_2 (Callable[[float], float]): The second function g(x).
derivative_1 (Callable[[float], float]): Derivative of the first function f'(x).
derivative_2 (Callable[[float], float]): Derivative of the second function g'(x).
evaluation_point (float): The point x at which to evaluate the Wronskian.

Returns:
float: Value of the Wronskian at the given point.
"""
return function_1(evaluation_point) * derivative_2(evaluation_point) - function_2(evaluation_point) * derivative_1(evaluation_point)

Check failure on line 87 in maths/wronskian_second_order_de.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

maths/wronskian_second_order_de.py:87:89: E501 Line too long (136 > 88)


def construct_general_solution(root1: complex, root2: complex) -> str:
"""
Construct the general solution based on the roots.

>>> construct_general_solution(2, 1)
'y(x) = C1 * e^(2x) + C2 * e^(1x)'
>>> construct_general_solution(3, 3)
'y(x) = (C1 + C2 * x) * e^(3x)'
>>> construct_general_solution(-1+2j, -1-2j)
'y(x) = e^(-1x) * (C1 * cos(2x) + C2 * sin(2x))'
"""
if isinstance(root1, complex) and root1.imag != 0:
alpha = round(root1.real, 10)
beta = round(abs(root1.imag), 10)
return f"y(x) = e^({alpha:g}x) * (C1 * cos({beta:g}x) + C2 * sin({beta:g}x))"
elif root1 == root2:
return f"y(x) = (C1 + C2 * x) * e^({root1:g}x)"
else:
return f"y(x) = C1 * e^({root1:g}x) + C2 * e^({root2:g}x)"


def analyze_differential_equation(a: float, b: float, c: float) -> None:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide descriptive name for the parameter: a

Please provide descriptive name for the parameter: b

Please provide descriptive name for the parameter: c

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a, b, c are explained to be coefficients.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide descriptive name for the parameter: a

Please provide descriptive name for the parameter: b

Please provide descriptive name for the parameter: c

"""
Analyze the DE and print the roots, type, and general solution.
a, b, c -> coefficients

Check failure on line 115 in maths/wronskian_second_order_de.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W293)

maths/wronskian_second_order_de.py:115:1: W293 Blank line contains whitespace
>>> analyze_differential_equation(1, -3, 2) # doctest: +ELLIPSIS
Characteristic Roots: (2.0, 1.0)
Solution Type: Distinct Real Roots
General Solution: y(x) = C1 * e^(2x) + C2 * e^(1x)
"""
roots = compute_characteristic_roots(a, b, c)
root1, root2 = roots
sol_type = classify_solution_type(root1, root2)
general_solution = construct_general_solution(root1, root2)

print(f"Characteristic Roots: ({root1:.1f}, {root2:.1f})")
print(f"Solution Type: {sol_type}")
print(f"General Solution: {general_solution}")


def main() -> None:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there is no test file in this pull request nor any test function or class in the file maths/wronskian_second_order_de.py, please provide doctest for the function main

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are no doctests required for main function.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there is no test file in this pull request nor any test function or class in the file maths/wronskian_second_order_de.py, please provide doctest for the function main

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there is no test file in this pull request nor any test function or class in the file maths/wronskian_second_order_de.py, please provide doctest for the function main

"""
Main function to run the second-order differential equation Wronskian analysis.

Interactive input is expected, so this function is skipped in doctests.
"""
print("Enter coefficients for the equation a*y'' + b*y' + c*y = 0")

# Skipping main in doctests because input() cannot be tested directly
a = float(input("a = ").strip()) # doctest: +SKIP
b = float(input("b = ").strip()) # doctest: +SKIP
c = float(input("c = ").strip()) # doctest: +SKIP

if a == 0:
print("Invalid input: coefficient 'a' cannot be zero.")
return

analyze_differential_equation(a, b, c)


if __name__ == "__main__":
main() # doctest: +SKIP

Loading