To solve this problem, we first need to define our variables and the objective function. Let x be the fraction of each liter that is cheap paint and y be the fraction that is expensive paint. The quality rating of the mix should be at least 80.

The quality rating of the mix can be calculated as 50x + 90y, since x is the fraction of cheap paint (quality rating 50) and y is the fraction of expensive paint (quality rating 90). We want this to be greater than or equal to 80.

Since we are mixing paints, x + y = 1, because the total fraction of the mix must add up to 1 (or 100%).

The cost per liter of the mix can be calculated as 0.30x + 1.50y, since x is the fraction of cheap paint ($0.30 per liter) and y is the fraction of expensive paint ($1.50 per liter). We want to minimize this cost.

We will use Gurobi, a linear programming solver, to find the optimal mix of paints that minimizes the cost while meeting the quality requirement.

Here's how we can translate this problem into a mathematical formulation:

- Minimize: 0.30x + 1.50y (cost per liter)
- Subject to:
  - 50x + 90y >= 80 (quality rating constraint)
  - x + y = 1 (mix fraction constraint)
  - x, y >= 0 (non-negativity constraints)

Now, let's implement this in Gurobi using Python:

```python
from gurobipy import *

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

# Create variables
x = m.addVar(vtype=GRB.CONTINUOUS, name="cheap_paint_fraction", lb=0)
y = m.addVar(vtype=GRB.CONTINUOUS, name="expensive_paint_fraction", lb=0)

# Set the objective function
m.setObjective(0.30*x + 1.50*y, GRB.MINIMIZE)

# Add constraints
m.addConstr(50*x + 90*y >= 80, "quality_rating")
m.addConstr(x + y == 1, "mix_fraction")

# Optimize model
m.optimize()

# Print solution
if m.status == GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f"Fraction of cheap paint: {x.x}")
    print(f"Fraction of expensive paint: {y.x}")
    print(f"Minimum cost per liter: ${m.objVal:.2f}")
else:
    print("No optimal solution found")
```