To solve the optimization problem described, we first need to translate the natural language description into a symbolic representation. This involves defining variables for each item (ferns, petunias, strawberry bushes), formulating the objective function using these variables, and listing all constraints in terms of these variables.

Let's denote:
- \(x_1\) as the number of ferns,
- \(x_2\) as the number of petunias,
- \(x_3\) as the number of strawberry bushes.

The objective function to maximize is: \(4x_1 + 5x_2 + 4x_3\).

Constraints:
1. The total combined beauty rating from petunias and strawberry bushes must be at least 49: \(12x_2 + 4x_3 \geq 49\).
2. \(-8x_1 + 4x_3 \geq 0\).
3. The total combined beauty rating from petunias plus strawberry bushes must be as much or less than 180: \(12x_2 + 4x_3 \leq 180\).
4. The total combined beauty rating from ferns plus strawberry bushes must be at most 89: \(9x_1 + 4x_3 \leq 89\).
5. The total combined beauty rating from ferns, petunias, and strawberry bushes has to be at most 89: \(9x_1 + 12x_2 + 4x_3 \leq 89\).
6. All variables must be whole numbers (integer constraints): \(x_1, x_2, x_3 \in \mathbb{Z}\).

Given this, the symbolic representation of the problem is:
```json
{
    'sym_variables': [('x1', 'ferns'), ('x2', 'petunias'), ('x3', 'strawberry bushes')],
    'objective_function': '4*x1 + 5*x2 + 4*x3',
    'constraints': [
        '12*x2 + 4*x3 >= 49',
        '-8*x1 + 4*x3 >= 0',
        '12*x2 + 4*x3 <= 180',
        '9*x1 + 4*x3 <= 89',
        '9*x1 + 12*x2 + 4*x3 <= 89'
    ]
}
```

Now, to implement this problem in Gurobi, we use the following Python code:

```python
from gurobipy import *

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

# Create variables
x1 = m.addVar(vtype=GRB.INTEGER, name="ferns")
x2 = m.addVar(vtype=GRB.INTEGER, name="petunias")
x3 = m.addVar(vtype=GRB.INTEGER, name="strawberry_bushes")

# Set the objective function
m.setObjective(4*x1 + 5*x2 + 4*x3, GRB.MAXIMIZE)

# Add constraints
m.addConstr(12*x2 + 4*x3 >= 49, "min_beauty_petunias_strawberry")
m.addConstr(-8*x1 + 4*x3 >= 0, "ferns_strawberry_constraint")
m.addConstr(12*x2 + 4*x3 <= 180, "max_beauty_petunias_strawberry")
m.addConstr(9*x1 + 4*x3 <= 89, "max_beauty_ferns_strawberry")
m.addConstr(9*x1 + 12*x2 + 4*x3 <= 89, "total_max_beauty")

# Optimize the model
m.optimize()

# Print the results
for v in m.getVars():
    print('%s %g' % (v.varName, v.x))

print('Obj: %g' % m.objVal)
```