To solve this optimization problem, we need to define the decision variables, objective function, and constraints. Let's denote:

- \(x_s\) as the amount invested in the son's company.
- \(x_f\) as the amount invested in the friend's company.

The objective is to maximize earnings. The earnings from each investment can be calculated by multiplying the amount invested by the return rate. Thus, the total earnings \(E\) can be expressed as:

\[E = 0.08x_s + 0.10x_f\]

The constraints are:
1. The total amount invested cannot exceed $50,000: \(x_s + x_f \leq 50000\).
2. The amount invested in the son's company must be at least three times as much as the amount invested in the friend's company: \(x_s \geq 3x_f\).
3. The amount invested in the son's company cannot exceed $40,000: \(x_s \leq 40000\).
4. Non-negativity constraints: \(x_s \geq 0\) and \(x_f \geq 0\), since we cannot invest a negative amount.

Given these definitions and constraints, we can set up the optimization problem in Gurobi using Python:

```python
from gurobipy import *

# Create a new model
m = Model("Investment_Optimization")

# Define the decision variables
x_s = m.addVar(lb=0, name="son_investment")
x_f = m.addVar(lb=0, name="friend_investment")

# Define the objective function: Maximize earnings
m.setObjective(0.08*x_s + 0.10*x_f, GRB.MAXIMIZE)

# Add constraints
m.addConstr(x_s + x_f <= 50000, name="total_investment")
m.addConstr(x_s >= 3*x_f, name="son_to_friend_ratio")
m.addConstr(x_s <= 40000, name="max_son_investment")

# Optimize the model
m.optimize()

# Print the results
if m.status == GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f" Invest in son's company: ${x_s.x:.2f}")
    print(f" Invest in friend's company: ${x_f.x:.2f}")
    print(f" Total earnings: ${m.objVal:.2f}")
else:
    print("No optimal solution found")
```