Skip to content

Migrate to Pyoframe v1.0

Pyoframe version 1.0 brings major improvements 🎉

  • New features including support for the Ipopt solver, the COPT solver, and the ability to improve performance with pf.Config.maintain_order=False.
  • Bug fixes: notably with Variable(equals=…) not properly parsing DataFrames.
  • Improved performance after rewriting internal functions based on extensive benchmarking.
  • Smoother developer experience, notably error messages explain the mathematical operations that led to an error and Pyoframe objects now print as legible tables.
  • Improved documentation including a revamped API page, an examples page, new explanations of functions like .over(…), and the ability to view previous versions of the documentation.
  • More readable syntax like our decision to rename add_dim to over and the changes to .sum() (see below).

Unfortunately, these improvements involve some breaking changes. This page will guide you in migrating your code to v1.0.

Breaking changes

Summary:

  1. pf.Model(…) has a new signature
  2. pf.sum(…, obj) was replaced by obj.sum(…)
  3. add_dim(…) was renamed to over(…)
  4. keep_unmatched() and drop_unmatched were renamed to keep_extras() and drop_extras()
  5. Other less common breaking changes

Follow the steps below to upgrade to v1.0.

1. Update all calls to pf.Model(…)

The signature of pf.Model(…) has changed from

Model(name=None, solver=None, solver_env=None, use_var_names=False, sense=None)

to

Model(solver=None, solver_env=None, *, name=None, solver_uses_variable_names=False, print_uses_variable_names=True, sense=None)

Importantly, notice how,

  • name is now a named-only parameter (you must write name=), and

  • use_var_names was renamed to solver_uses_variable_names (to avoid confusion with the new option print_uses_variable_names).

Please update all calls to pf.Model(…) accordingly.

import pyoframe as pf

pf.Model("my-model")  # before
pf.Model(name="my-model")  # after

pf.Model(use_var_names=True)  # before
pf.Model(solver_uses_variable_names=True)  # after

2. Replace pf.sum(…, obj) with obj.sum(…)

To improve readability and align with Python best practices, both pf.sum(…, obj) and pf.sum_by(…, obj) have been replaced by obj.sum(…) and obj.sum_by(…). Here obj represents any Pyoframe Variable or Expression.1 For example:

pf.sum(m.X)  # before
m.X.sum()  # after

pf.sum(["day", "hour"], m.X)  # before
m.X.sum("day", "hour")  # after

pf.sum_by("city", m.X)  # before
m.X.sum_by("city")  # after

Please update all uses of pf.sum and pf.sum_by accordingly.

3. Rename .add_dim(…) to .over(…)

For clarity, .add_dim(…) was renamed to .over(…). Please update your code accordingly.

m.X.add_dim("time")  # before
m.X.over("time")  # after

4. Rename .keep_unmatched() and .drop_unmatched() with .keep_extras() and .drop_extras()

For clarity, .keep_unmatched() and .drop_unmatched() were renamed to .keep_extras() and .drop_extras(), respectively. Please update your code accordingly.

m.X.keep_unmatched()  # before
m.X.keep_extras()  # after

m.X.drop_unmatched()  # before
m.X.drop_extras()  # after

5. Review code for other breaking changes

Steps 1 to 3 should cover most breaking changes. For completeness, below are some additional breaking changes that you are less likely to encounter.

  1. All submodules (e.g. pyoframe.core) and some attributes (obj.unmatched_strategy, obj.allowed_new_dims, obj.dimensions_unsafe, Config.enable_is_duplicated_expression_safety_check) have been renamed to begin with an underscore (e.g. now pyoframe._core) to indicate that these elements are no longer part of the public API.

  2. If you try to read model.objective before having defined an objective, an error will be raised. Prior to v1.0 None was returned.

  3. .over(…), .keep_extras(), and .drop_extras() now have a well-defined "order of operations." They must be applied after all other transforms. For example, my_obj.drop_extras().sum("time") no longer works because my_obj.sum("time").drop_extras() should be used instead. Learn more.

  4. pf.Config.print_max_set_elements, pf.Config.print_max_line_length, and pf.Config.print_max_lines no longer exist. Use pf.Config.print_max_terms pf.Config.print_polars_config instead.

  5. pf.Config.print_uses_variable_names no longer exists. Use the equivalent option in pf.Model(…)

  6. pf.Config.default_solver now defaults to "auto" instead of None to clarify that by default, the solver will be automatically detected. Don't set pf.Config.default_solver = None.

  7. pf.Config.disable_unmatched_checks has been renamed to pf.Config.disable_extras_checks.

  8. Gurobi users: Model.write(…) is now only available when solver_uses_variable_names=True.

  9. Previously, passing a DataFrame to the equals= parameter of the Variable constructor would result in a Variable equal to 1. This bug has been fixed.


  1. If you were using pf.sum or pf.sum_by on a Polars or Pandas DataFrame instead of on a Pyoframe object, you should use the .sum() function native to Pandas/Polars.