To solve the given optimization problem, we first need to convert the natural language description into a symbolic representation. This involves defining variables for the quantities of pills and cream, formulating the objective function based on profit, and establishing constraints related to the time available for weighing and packaging.

Let's define:
- $x_1$ as the number of bottles of pills.
- $x_2$ as the number of bottles of cream.

The objective function aims to maximize profit. Given that each bottle of pills yields a profit of $50 and each bottle of cream yields a profit of $60, the objective function can be written as:
\[ \text{Maximize: } 50x_1 + 60x_2 \]

The constraints are based on the time available for weighing and packaging. Each bottle of pills requires 20 minutes of weighing and 10 minutes of packaging, while each bottle of cream requires 15 minutes of weighing and 15 minutes of packaging. The pharmacy has 4000 minutes available for weighing and 3000 minutes available for packaging. Thus, the constraints can be formulated as:
1. Weighing time constraint: $20x_1 + 15x_2 \leq 4000$
2. Packaging time constraint: $10x_1 + 15x_2 \leq 3000$

Additionally, since the number of bottles cannot be negative, we have non-negativity constraints:
- $x_1 \geq 0$
- $x_2 \geq 0$

Symbolic representation in JSON format:
```json
{
    'sym_variables': [('x1', 'number of bottles of pills'), ('x2', 'number of bottles of cream')],
    'objective_function': '50*x1 + 60*x2',
    'constraints': ['20*x1 + 15*x2 <= 4000', '10*x1 + 15*x2 <= 3000', 'x1 >= 0', 'x2 >= 0']
}
```

To solve this problem using Gurobi in Python, we'll write the following code:

```python
from gurobipy import *

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

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

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

# Add constraints
m.addConstr(20*x1 + 15*x2 <= 4000, name="weighing_time_constraint")
m.addConstr(10*x1 + 15*x2 <= 3000, name="packaging_time_constraint")

# Optimize the model
m.optimize()

# Print the results
if m.status == GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f"Number of bottles of pills: {x1.x}")
    print(f"Number of bottles of cream: {x2.x}")
    print(f"Maximum profit: ${m.objVal}")
else:
    print("No optimal solution found")
```