Getting Started with MoselPy
Topics covered in this chapter:
Introduction
MoselPy combines Python's ecosystem with Mosel's optimization capabilities, leveraging Mosel's performance, portability, and security characteristics while enabling Python-based data preparation, result analysis, and workflow integration.
When to use MoselPy
Use MoselPy when you:
- have existing Mosel models and want to integrate them into Python applications
- prefer Mosel's algebraic modeling syntax, which closely mimics mathematical notation, for clarity and maintainability
- need pre-compiled models for performance optimization or intellectual property protection
- want to leverage Mosel's efficient integration with Xpress Solver while using Python for data preparation
- require seamless Pandas Series and DataFrames integration for input/output in data science workflows
- need iterative solving with Python-based logic between solves, combining Mosel's solving power with Python's flexibility
- are building data-driven optimization workflows that benefit from Mosel's efficiency and dynamic object support.
Prerequisites
MoselPy requires:
- Python 3.10 to 3.14 (standard builds). Free-threaded Python (3.13t, 3.14t) is not currently supported.
- FICO Xpress installation (a solver license is only required to run optimization models; the Mosel runtime itself is license-free). The Xpress Solver libraries are only required when the Mosel model uses mmxprs or mmxnlp.
- Optional: Pandas (for Series and DataFrame integration)
Installation
Install MoselPy via PyPI:
pip install moselpy
Verify installation:
import moselpy as mp
print(f"MoselPy version: {mp.version}")
print(f"Mosel version: {mp.mosel_version}")
print(f"Xpress version: {mp.xpress_version}")
The package includes the Python bindings; the Mosel runtime and Xpress Optimizer libraries are provided by your Xpress installation (see the Xpress Installation Guide).
Runtime dependencies. MoselPy requires xprm_rt (Mosel runtime) and xprm_mc (Mosel compiler) from your Xpress installation. If a Mosel model loads Mosel libraries (BIM or DSO files) these also need to be present with their respective dependencies as indicated in the secrtdep.
Environment variables
MoselPy uses the standard Xpress environment variables, which are automatically set up by the Xpress installers. The following are relevant when running MoselPy:
- XPRESSDIR
- Path to the Xpress installation directory. Required if Xpress is not installed in the default location.
- XPAUTH_PATH
- Path to the solver license file ( xpauth.xpr). Searched in standard locations if not specified. Only needed for solver-based models.
Note on Jupyter notebooks. Jupyter spawns a separate kernel process that may not inherit the full system environment. This causes solver modules such as mmxprs to be not found even when XPRESSDIR is correctly set. If you encounter this, add the following line at the top of your notebook setup cell, before importing moselpy:
import os
os.environ.setdefault('XPRESSDIR', '/path/to/xpressmp')
Your first model
We start with a simple Mosel model that prints a message. Create a file hello.mos:
model HelloWorld
writeln("Hello from Mosel!")
end-model
Execute it from Python:
import moselpy as mp
# One-step: compile, load, and run
mp.execute_model("hello.mos")
Output: Hello from Mosel!
The three-step workflow
For more control, use the three-step workflow: compile, load, run. The key functions are compile_model() to generate a compiled BIM file, load_model() to load it into memory, and run() to execute the model:
import moselpy as mp
# 1. Compile: .mos -> .bim
mp.compile_model("hello.mos", "hello.bim")
# 2. Load compiled model
model = mp.load_model("hello.bim")
# 3. Run
model.run()
print(f"Model '{model.name}' completed with status: {model.exec_status}")
Output:
Hello from Mosel! Model 'HelloWorld' completed with status: ExecStatus.OK
The compiled .bim file is platform-independent and can be reused without recompilation, providing faster execution and intellectual property protection by hiding the model source code.
Model parameters
Pass parameters to Mosel models at execution time using the exec_params argument in the run() method. Parameters defined in the Mosel parameters block can be overridden from Python. Create greeting.mos:
model Greeting
parameters
NAME = "World"
end-parameters
writeln("Hello, ", NAME, "!")
end-model
Run with different parameters:
import moselpy as mp
model = mp.load_model("greeting.bim")
# Method 1: Dict (recommended)
model.run(exec_params={"NAME": "Python"})
# Method 2: String
model.run(exec_params="NAME=MoselPy")
Output:
Hello, Python! Hello, MoselPy!
The dict format handles special characters automatically and is the preferred option.
Stream redirection
Control where Mosel output goes using set_default_stream(). See Chapter Model Class for the full API reference of Model.set_default_stream().
Silencing output
Redirect output to the built-in null: driver to suppress it:
import moselpy as mp
model = mp.load_model("model.bim")
model.set_default_stream(mp.StreamType.OUTPUT, "null:")
model.set_default_stream(mp.StreamType.ERROR, "null:")
model.run()
Capturing output
Redirect output to a Python buffer using the moselpy: driver. Register a StringIO object in the model's symbol registry, then route the stream through it. The buffer must be set before calling run():
import moselpy as mp
from io import StringIO
model = mp.load_model("model.bim")
buf = StringIO()
model.symbols["out"] = buf
model.set_default_stream(mp.StreamType.OUTPUT, "moselpy:out")
model.run()
del model.symbols["out"]
print(f"Captured: {buf.getvalue()}")
Output: Captured: Hello from Mosel!
Global vs per-model redirection
Global stream settings affect all models that have not overridden their own streams. Per-model settings take precedence. Note that global settings via config.set_default_stream() must be set before the first load_model() call in the process. Once a model is loaded, only per-model set_default_stream() calls are possible:
import moselpy as mp
# Global: must be called BEFORE the first load_model()
mp.config.set_default_stream(mp.StreamType.OUTPUT, "null:")
# Per-model: can be called at any time, overrides global for this model only
model = mp.load_model("model.bim")
model.set_default_stream(mp.StreamType.OUTPUT, "output.txt")
Pass None as the file name to restore the default stream (sys.stdout, sys.stderr, or sys.stdin).
Running a model from a string
For quick prototyping or generated models, you can define Mosel source directly as a Python string. Since compile_model() and execute_model() expect a file path, write the source to a temporary file, compile, run, then clean up:
import moselpy as mp
import tempfile, os
src = """model SumToN
declarations
n: integer
result: real
end-declarations
initializations from "moselpy:"
n
end-initializations
result := n * (n + 1) / 2.0
initializations to "moselpy:"
result
end-initializations
end-model"""
with tempfile.NamedTemporaryFile(mode="w", suffix=".mos", delete=False) as f:
f.write(src)
tmp_mos = f.name
tmp_bim = tmp_mos[:-4] + ".bim"
try:
mp.compile_model(tmp_mos, tmp_bim)
model = mp.load_model(tmp_bim)
output = model.run(input_data={"n": 100})
print(f"Sum 1..100 = {output['result']:.0f}")
finally:
for p in [tmp_mos, tmp_bim]:
if os.path.exists(p):
os.unlink(p)
Output:
Sum 1..100 = 5050
This pattern is useful for testing small models interactively or for dynamically generating Mosel source from Python.
© 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.
