Initializing help system before first use

Optimization Workflows

Topics covered in this chapter:

Running optimization models

We demonstrate with a multi-period production planning problem: a factory produces multiple products over several months, and we want to maximize profit while respecting monthly capacity. This example also shows how Pandas DataFrames and Series are passed as input and how multiple output arrays are returned.

Mosel model

Create production.mos. The model receives sets, Series, and a DataFrame from Python through initializations from "moselpy:", and returns four output arrays plus the total profit. Note how Mosel's syntax closely resembles mathematical notation:

model "multiperiod_production"
  uses "mmxprs"

  parameters
    DATASOURCE = "production.dat"
  end-parameters

  public declarations
    PRODUCTS: set of string
    MONTHS: set of string

    Profit: array(PRODUCTS) of real
    Demand: array(PRODUCTS, MONTHS) of real
    Capacity: array(MONTHS) of real
    Duration: array(PRODUCTS) of real

    produce: array(PRODUCTS, MONTHS) of mpvar
    CapLimit: array(MONTHS) of linctr

    ! Output arrays
    Production: array(PRODUCTS, MONTHS) of real
    MonthlyRevenue: array(MONTHS) of real
    ProductRevenue: array(PRODUCTS) of real
    CapacityUsed: array(MONTHS) of real
    TotalProfit: real
  end-declarations

  initializations from DATASOURCE
    Profit Demand Capacity Duration
  end-initializations

  ! Upper bound: production cannot exceed demand (lower bound defaults to 0)
  forall(p in PRODUCTS, m in MONTHS) produce(p, m) <= Demand(p, m)

  ! Capacity constraint per month (naming enables getact after solve)
  forall(m in MONTHS)
    CapLimit(m) := sum(p in PRODUCTS) Duration(p) * produce(p, m) <= Capacity(m)

  ! Maximize profit
  maximize(sum(p in PRODUCTS, m in MONTHS) Profit(p) * produce(p, m))

  ! Store results
  TotalProfit := getobjval
  forall(p in PRODUCTS, m in MONTHS) Production(p, m) := getsol(produce(p, m))
  forall(m in MONTHS) do
    MonthlyRevenue(m) := sum(p in PRODUCTS) Profit(p) * getsol(produce(p, m))
    CapacityUsed(m) := getact(CapLimit(m))
  end-do
  forall(p in PRODUCTS) ProductRevenue(p) := sum(m in MONTHS) Profit(p) * getsol(produce(p, m))

  initializations to DATASOURCE
    TotalProfit Production MonthlyRevenue ProductRevenue CapacityUsed
  end-initializations
end-model

Python code

Build input data using Pandas. A DataFrame encodes demand by product and month; Series encode per-product and per-month scalar data. Pass them directly to run(). MoselPy converts them automatically, using the index as the set dimension:

import pandas as pd
import moselpy as mp

products = ["Widgets", "Gadgets", "Gizmos"]
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]

# Demand varies by product and month (2D -> Mosel array(PRODUCTS, MONTHS))
demand_df = pd.DataFrame({
    "Jan": [100, 80, 60], "Feb": [120, 90, 70], "Mar": [140, 100, 80],
    "Apr": [130, 95, 75], "May": [110, 85, 65], "Jun": [90, 70, 55],
}, index=products)

profit_series   = pd.Series({"Widgets": 25, "Gadgets": 40, "Gizmos": 55})
duration_series = pd.Series({"Widgets": 1.0, "Gadgets": 1.5, "Gizmos": 2.0})
capacity_series = pd.Series({
    "Jan": 200, "Feb": 220, "Mar": 240, "Apr": 230, "May": 210, "Jun": 180
})

mp.compile_model("production.mos", "production.bim")
model = mp.load_model("production.bim")
model.set_default_stream(mp.StreamType.OUTPUT, "null:")

output = model.run(
    exec_params={"DATASOURCE": "moselpy:"},
    input_data={
        "Profit": profit_series,
        "Demand": demand_df,
        "Capacity": capacity_series,
        "Duration": duration_series,
    })

print(f"Total Profit: ${output['TotalProfit']:,.2f}")

Output: Total Profit: $34,808.33

The full output arrays (Production, MonthlyRevenue, ProductRevenue, CapacityUsed) are also available in output. See Section Accessing results for how to retrieve and display them.

Problem status

After running an optimization model, check the execution status using the exec_status attribute and the optimization status using the prob_status object before trying to access any solution information. The exec_status indicates whether the model executed without errors, while prob_status indicates whether a solution is available:

import moselpy as mp

model = mp.load_model("model.bim")
output = model.run(input_data=data)

# Check execution status
if model.exec_status == mp.ExecStatus.OK:
    print("Model executed successfully")

    # Check optimization status
    if model.prob_status.is_solution_optimal:
        print("Optimal solution found")
    elif model.prob_status.is_problem_infeasible:
        print("Problem is infeasible")
    elif model.prob_status.is_problem_unbounded:
        print("Problem is unbounded")
    else:
        print("No optimal solution found")
else:
    print(f"Execution error: {model.exec_status}")

Output:

Model executed successfully
Optimal solution found

The prob_status object provides boolean properties:

  • is_solution_optimal
  • is_solution_available
  • is_problem_infeasible
  • is_problem_unbounded
  • is_optimization_finished
  • is_optimization_failed
  • is_problem_valid

If the model does not call maximize or minimize, prob_status.status_code returns -1 and all is_* properties are set to False. For the full list of status codes, see the Xpress Optimizer Reference Manual.

The objective function value is available directly via model.objective_value. The exit code from the Mosel model (set via exit statement, defaults to 0 indicating successful execution) is available as model.exit_code. See Chapter Model Class for the full reference.

Accessing results

There are two methods to retrieve results after running a model. Always check the prob_status object before trying to access any solution information. For converting results to Pandas Series and DataFrames, see Section Pandas integration.

Method 1: run() return value

The run() method returns a dictionary containing all variables explicitly output by the Mosel model via initializations to "moselpy:". Scalars are returned as Python scalars; arrays are returned as dicts.

output = model.run(input_data=data)

# Scalar
profit = output['TotalProfit']
print(f"Total Profit: ${profit:,.2f}")

# 1D array -> dict
product_revenue = output['ProductRevenue']  # {'Gadgets': ..., 'Gizmos': ..., 'Widgets': ...}

# 2D array -> dict with tuple keys
production = output['Production']  # {('Gadgets','Jan'): ..., ('Gizmos','Jan'): ..., ...}

Output:

Total Profit: $34,808.33

Only variables listed in the Mosel initializations to "moselpy:" block are included in the output dict. Use Method 2 to access any other model entity after the solve.

Method 2: find_identifier()

find_identifier() retrieves the value of any public model entity by name after a solve, including variables not in the output block. It can return raw Python values or Pandas objects. Entities must be declared with the explicit public keyword in the Mosel source. When the model is compiled with the g or G flag, all global symbols become visible:

model.run(input_data=data)

# Scalar
profit = model.find_identifier("TotalProfit")
print(f"TotalProfit: {profit:.2f}")

# 1D array as dict (default)
product_revenue = model.find_identifier("ProductRevenue")
print(product_revenue)

# 1D array as Pandas Series
product_revenue_series = model.find_identifier("ProductRevenue", use_pandas=True)
print(product_revenue_series)

Output:

TotalProfit: 34808.33
{'Gadgets': 12533.33, 'Gizmos': 22275.0, 'Widgets': 0.0}
Gadgets    12533.33
Gizmos     22275.00
Widgets        0.00
dtype: float64

Use find_identifier() when you need to inspect intermediate variables or parameters that are not part of the model output, for example for debugging or post-solve analysis.

Visualizing results

Optimization results from MoselPy integrate naturally with matplotlib, enabling production dashboards and analysis charts. This section continues the multi-period production example from the previous section.

Retrieving results as Pandas objects

Use find_identifier() with the use_pandas=True flag to retrieve output arrays as Pandas objects. For 1D arrays it returns a Series; for 2D arrays it returns a Series with a MultiIndex, which can be unstacked into a DataFrame:

# 1D arrays -> Series
monthly_revenue = model.find_identifier("MonthlyRevenue", use_pandas=True)
product_revenue = model.find_identifier("ProductRevenue", use_pandas=True)
capacity_used   = model.find_identifier("CapacityUsed", use_pandas=True)

# 2D array -> MultiIndex Series, unstack to DataFrame
production_df = model.find_identifier("Production", use_pandas=True).unstack(level=1)
print(production_df)

Output:

           Jan    Feb    Mar    Apr    May    Jun
Widgets   0.00   0.00   0.00   0.00   0.00   0.00
Gadgets  53.33  53.33  53.33  53.33  53.33  46.67
Gizmos   60.00  70.00  80.00  75.00  65.00  55.00

Production dashboard

Plot a four-panel chart: monthly production by product (stacked bars), revenue share by product (pie), capacity utilization over time (fill chart), and monthly revenue trend (bar chart):

import matplotlib.pyplot as plt

fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 1. Production by product (stacked bar)
production_df.T.plot(kind="bar", stacked=True, ax=axes[0, 0], colormap="viridis")
axes[0, 0].set_title("Monthly Production by Product")
axes[0, 0].set_xlabel("Month")
axes[0, 0].set_ylabel("Units")
axes[0, 0].tick_params(axis='x', rotation=45)

# 2. Revenue by product (pie)
product_revenue_ordered = product_revenue.reindex(products)
axes[0, 1].pie(product_revenue_ordered, labels=products,
               autopct='%1.1f%%', startangle=90)
axes[0, 1].set_title("Revenue Share by Product")

# 3. Capacity utilization (fill + line)
capacity_ordered = capacity_series.reindex(months)
used_ordered = capacity_used.reindex(months)
x = range(len(months))
axes[1, 0].fill_between(x, capacity_ordered, alpha=0.3, label="Available")
axes[1, 0].plot(x, used_ordered, 'o-', linewidth=2, markersize=8, label="Used")
axes[1, 0].set_xticks(x)
axes[1, 0].set_xticklabels(months)
axes[1, 0].set_title("Capacity Utilization")
axes[1, 0].legend()

# 4. Monthly revenue (bar)
monthly_ordered = monthly_revenue.reindex(months)
axes[1, 1].bar(months, monthly_ordered, color="steelblue")
axes[1, 1].set_title("Monthly Revenue")
axes[1, 1].set_ylabel("Revenue ($)")

plt.tight_layout()
plt.show()
MoselPy/moselpy-dashboard

Figure 4.1: Production dashboard: monthly production by product, revenue share, capacity utilization, and monthly revenue


© 2001-2026 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.