Excel’s Goal Seek changes one input cell to achieve a desired value in a formula cell. Python’s fsolve does that, but it can also solve for multiple input values simultaneously.
Python fsolve:
fsolve returns a numpy array. The elements of that array are extracted as arr[0], arr[1], etc.
Find the values of x that make x² - 5x + 6 = 0
We know from algebra (factoring or quadratic formula) that there are two solutions: x = 2 and x = 3.
# STEP 1: Define a function that is zero at the value we want to find
def function_to_find_zero(x):
return x**2 - 5*x + 6
# STEP 2: Find solution(s). Vary initial values to look for multiple solutions.
initial_value1 = 0
solution1 = fsolve(function_to_find_zero, initial_value1)
initial_value2 = 5
solution2 = fsolve(function_to_find_zero, initial_value2)
print(f"Starting near 0: solution = {solution1}")
print(f"Starting near 5: solution = {solution2}")Starting near 0: solution = [2.]
Starting near 5: solution = [3.]
Exercise 1 (with Gemini): Ask Gemini to “use fsolve to find the value of x where x^2 - 9 = 0”
Exercise 2 (on your own): Type from scipy.optimize import fsolve then fsolve(lambda x: x - 5, 0) and run it to find where x - 5 = 0.
What annual sales volume gives NPV = 0?
Project assumptions:
def calculate_npv(annual_qty):
"""Calculate NPV for a given annual sales quantity"""
cash_flows = [-initial_capex] # Year 0: Initial investment
nwc_balances = []
for year in range(1, project_life + 1):
# Annual financial calculations
sales = annual_qty * unit_price
cogs = annual_qty * cogs_per_unit
gross_profit = sales - cogs
ebitda = gross_profit - sga_annual
ebit = ebitda - annual_depreciation
net_income = ebit * (1 - tax_rate)
# Net Working Capital
nwc = annual_qty * nwc_per_unit
nwc_balances.append(nwc)
if year == 1:
change_nwc = nwc
else:
change_nwc = nwc - nwc_balances[year-2]
# Cash Flow
if year < project_life:
cash_flow = net_income + annual_depreciation - change_nwc
else:
cash_flow = net_income + annual_depreciation + nwc
cash_flows.append(cash_flow)
# Calculate NPV
npv = 0
for year, cf in enumerate(cash_flows):
npv += cf / (1 + discount_rate)**year
return npv# We want to set NPV = 0, so we can use calculate_npv as function_to_find_zero
initial_guess = 4000
breakeven_qty = fsolve(calculate_npv, initial_guess)[0]
print(f"Required Annual Sales Volume: {breakeven_qty:,.0f} units")
# Verify NPV = 0
verification_npv = calculate_npv(breakeven_qty)
print(f"NPV at break-even volume: ${verification_npv:,.2f}")Required Annual Sales Volume: 1,420 units
NPV at break-even volume: $0.00
While fsolve finds where functions equal zero, minimize finds where functions reach their lowest value.
Important attributes of the result object:
result.success: was the minimization successful (True or False)result.x: the value of x that minimizes the functionresult.fun: the value of the function at the minimumFind the value of x that minimizes f(x) = x² - 4x + 7
def function_to_minimize(x):
return x**2 - 4*x + 7
initial_guess = 0
result = minimize(function_to_minimize, initial_guess)
print(f'Was the minimization successful? {result.success}')
print(f'What is the minimizing value of x? {result.x}')
print(f'What is the value of the function at the minimum? {result.fun}')Was the minimization successful? True
What is the minimizing value of x? [2.00000002]
What is the value of the function at the minimum? 3.0
Exercise 1 (with Gemini): Ask Gemini to “use scipy.optimize.minimize to find the minimum of the function f(x) = x^2 + 3x + 2”
Exercise 2 (on your own): Type from scipy.optimize import minimize then result = minimize(lambda x: x**2, 5) then print(result.x) and run it.
To solve a maximize problem, we minimize the negative of the function.
Key Concept: The x value that minimizes -f(x) is the SAME x value that maximizes f(x)
Example: