To solve this optimization problem, we first need to convert the natural language description into a symbolic representation. Let's define two variables:

- $x_1$ represents the amount of money invested in the action movie.
- $x_2$ represents the amount of money invested in the animation movie.

The objective function is to maximize earnings. Given that the money invested in the action movie earns 9% and the money invested in the animation movie earns 6%, the total earnings can be represented as:

\[ \text{Earnings} = 0.09x_1 + 0.06x_2 \]

The constraints are:
1. The total amount invested is $500,000: \( x_1 + x_2 = 500,000 \).
2. At least three times as much money is invested in the animation movie as in the action movie: \( x_2 \geq 3x_1 \).
3. The amount invested in the animation movie can be at most $400,000: \( x_2 \leq 400,000 \).

Symbolic representation:
```json
{
    'sym_variables': [('x1', 'amount invested in the action movie'), ('x2', 'amount invested in the animation movie')],
    'objective_function': '0.09*x1 + 0.06*x2',
    'constraints': ['x1 + x2 == 500000', 'x2 >= 3*x1', 'x2 <= 400000']
}
```

To solve this problem using Gurobi in Python, we'll use the following code:
```python
from gurobipy import *

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

# Define variables
x1 = m.addVar(lb=0, vtype=GRB.CONTINUOUS, name="Action_Movie")
x2 = m.addVar(lb=0, vtype=GRB.CONTINUOUS, name="Animation_Movie")

# Set the objective function
m.setObjective(0.09*x1 + 0.06*x2, GRB.MAXIMIZE)

# Add constraints
m.addConstr(x1 + x2 == 500000, "Total_Investment")
m.addConstr(x2 >= 3*x1, "Animation_VS_Action")
m.addConstr(x2 <= 400000, "Max_Animation")

# Optimize the model
m.optimize()

# Print the results
if m.status == GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f"Action Movie Investment: ${x1.x:.2f}")
    print(f"Animation Movie Investment: ${x2.x:.2f}")
    print(f"Total Earnings: ${m.objVal:.2f}")
else:
    print("No optimal solution found")
```