Skip to content

Improve API documentation #149

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ build
tmp
.coverage
.cache
docs/reference
dist
*.log
site
.idea
src/pyoframe/_version.py
htmlcov/
benchmarks/energy_model/data/
.snakemake/
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@
"Pyoframe",
"ipopt",
"highs"
],
"python.testing.pytestArgs": [
"."
]
}
2 changes: 1 addition & 1 deletion docs/examples/diet.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def solve_model():
m = solve_model()
```

1. `.drop_unmatched()` ensures that if `min_nutrient` is `null` for certain foods, no constraint will be created for those foods. [Learn more](../learn/getting-started/special-functions.md#expressiondrop_unmatched)
1. `.drop_unmatched()` ensures that if `min_nutrient` is `null` for certain foods, no constraint will be created for those foods. [Learn more](../learn/getting-started/special-functions.md#drop_unmatched-and-keep_unmatched)

So what should you eat...

Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ hide:
- toc
- actions
- footer
- feedback
template: home.html
---
# Pyoframe: A loved alternative to GAMS, Pyomo, and Linopy
Expand Down
26 changes: 26 additions & 0 deletions docs/javascripts/feedback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
document$.subscribe(function () {
const feedback = document.forms.feedback;
if (feedback === undefined) return;

feedback.hidden = false;

feedback.addEventListener("submit", function (ev) {
ev.preventDefault();

feedback.firstElementChild.disabled = true;

const data = ev.submitter.getAttribute("data-md-value");

if (data == 0) {
const commentElement = document.getElementById("comments");
commentElement.style.display = "block";
}

const note = feedback.querySelector(
".md-feedback__note [data-md-value='" + data + "']"
);
if (note) {
note.hidden = false;
}
});
});
4 changes: 1 addition & 3 deletions docs/learn/getting-started/special-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,5 @@ Pyoframe has a few special functions that make working with dataframes easy and

## `Expression.add_dim()`

## `Expression.drop_unmatched()`

## `Expression.keep_unmatched()`
## `drop_unmatched` and `keep_unmatched`

46 changes: 46 additions & 0 deletions docs/overrides/partials/comments.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<div id="comments" style="display: none;">
<h2 id="__comments">{{ lang.t("meta.comments") }}</h2>
<!-- Insert generated snippet here -->

<script src="https://giscus.app/client.js" data-repo="bravos-power/pyoframe" data-repo-id="R_kgDOLa_z1A"
data-category="Documentation Feedback" data-category-id="DIC_kwDOLa_z1M4Cr4es" data-mapping="pathname"
data-strict="0" data-reactions-enabled="0" data-emit-metadata="0" data-input-position="top"
data-theme="preferred_color_scheme" data-lang="en" crossorigin="anonymous" async>
</script>

<!-- Synchronize Giscus theme with palette -->
<script>
var giscus = document.querySelector("script[src*=giscus]")

// Set palette on initial load
var palette = __md_get("__palette")
if (palette && typeof palette.color === "object") {
var theme = palette.color.scheme === "slate"
? "transparent_dark"
: "light"

// Instruct Giscus to set theme
giscus.setAttribute("data-theme", theme) // (1)!
}

// Register event handlers after documented loaded
document.addEventListener("DOMContentLoaded", function () {
var ref = document.querySelector("[data-md-component=palette]")
ref.addEventListener("change", function () {
var palette = __md_get("__palette")
if (palette && typeof palette.color === "object") {
var theme = palette.color.scheme === "slate"
? "transparent_dark"
: "light"

// Instruct Giscus to change theme
var frame = document.querySelector(".giscus-frame")
frame.contentWindow.postMessage(
{ giscus: { setConfig: { theme } } },
"https://giscus.app"
)
}
})
})
</script>
</div>
Empty file.
3 changes: 3 additions & 0 deletions docs/reference/public.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Public API

::: pyoframe
3 changes: 3 additions & 0 deletions giscus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"origins": ["https://bravos-power.github.io"]
}
24 changes: 23 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ plugins:
python:
options:
show_if_no_docstring: true
summary: true
summary:
attributes: true
functions: true
classes: true
modules: false
extensions:
- dataclasses
merge_init_into_class: true
Expand All @@ -93,5 +97,23 @@ plugins:
scripts:
- scripts/gen_ref_pages.py

extra_javascript:
- javascripts/feedback.js

extra:
analytics:
provider: custom
feedback:
title: Was this page helpful?
ratings:
- icon: material/emoticon-happy-outline
name: This page was helpful
data: 1
note: Thanks for your feedback!
- icon: material/emoticon-sad-outline
name: This page could be improved
data: 0
note: Let us know how we can improve this page.

watch:
- src
4 changes: 2 additions & 2 deletions src/pyoframe/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""
Pyoframe's public API accessible via `import pyoframe`.

Info:
`import pyoframe` will automatically monkey patch Polars and Pandas
Tip:
`import pyoframe` will automatically patch Polars and Pandas
to make `DataFrame.to_expr()` available.
"""

Expand Down
136 changes: 122 additions & 14 deletions src/pyoframe/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,35 +84,141 @@ def __init__(cls, name, bases, dct):

class Config(metaclass=_ConfigMeta):
"""
Configuration options that apply to the entire library.
Global settings for Pyoframe (for advanced users).
"""

default_solver: SUPPORTED_SOLVER_TYPES | Solver | None = None
"""
The solver to use when `pf.Model()` is called without specifying a solver.
If default_solver is not set (`None`),
Pyoframe will choose the first solver in SUPPORTED_SOLVERS that doesn't produce an error.

There is no reason why you set the solver here instead of passing it to the Model constructor.
This is mainly used for testing purposes.
Setting for internal testing purposes only.

The solver to be used if no solver is specified while constructing a [Model][pyoframe.Model].
Overrides the default behavior of automatically trying to detect and use whichever solver is installed.
This setting is used for testing only; users should specify their preferred solver during Model construction.
"""

disable_unmatched_checks: bool = False
float_to_str_precision: Optional[int] = 5
print_uses_variable_names: bool = True
"""
Improve performance by skipping unmatched checks (not recommended).

When `True`, unmatched checks are disabled which effectively means that all expressions
are treated as if they contained [`.keep_unmatched()`][pyoframe.Expression.keep_unmatched]
(unless [`.drop_unmatched()`][pyoframe.Expression.drop_unmatched] was applied).

!!! warning
This might improve performance, but it will suppress the "unmatched" errors that alert developers to unexpected
behaviors (see [here](../../learn/getting-started/special-functions.md#drop_unmatched-and-keep_unmatched)).
Only consider enabling after you have thoroughly tested your code.

Examples:
>>> import polars as pl
>>> population = pl.DataFrame({"city": ["Toronto", "Vancouver", "Montreal"], "pop": [2_731_571, 631_486, 1_704_694]}).to_expr()
>>> population_influx = pl.DataFrame({"city": ["Toronto", "Vancouver", "Montreal"],"influx": [100_000, 50_000, None],}).to_expr()

Normally, an error warns users that the two expressions have conflicting indices:
>>> population + population_influx
Traceback (most recent call last):
...
pyoframe.constants.PyoframeError: Failed to add expressions:
<Expression size=3 dimensions={'city': 3} terms=3> + <Expression size=2 dimensions={'city': 2} terms=2>
Due to error:
Dataframe has unmatched values. If this is intentional, use .drop_unmatched() or .keep_unmatched()
shape: (1, 2)
┌──────────┬────────────┐
│ city ┆ city_right │
│ --- ┆ --- │
│ str ┆ str │
╞══════════╪════════════╡
│ Montreal ┆ null │
└──────────┴────────────┘

But if `Config.disable_unmatched_checks = True`, the error is suppressed and the sum is considered to be `population.keep_unmatched() + population_influx.keep_unmatched()`:
>>> pf.Config.disable_unmatched_checks = True
>>> population + population_influx
<Expression size=3 dimensions={'city': 3} terms=3>
[Toronto]: 2831571
[Vancouver]: 681486
[Montreal]: 1704694
"""

print_max_line_length: int = 80
"""
Maximum number of characters to print in a single line.

Examples:
>>> pf.Config.print_max_line_length = 20
>>> m = pf.Model()
>>> m.vars = pf.Variable({"x": range(1000)})
>>> pf.sum(m.vars)
<Expression size=1 dimensions={} terms=1000>
vars[0] + vars[1] + …

"""

print_max_lines: int = 15
"""
Maximum number of lines to print.

Examples:
>>> pf.Config.print_max_lines = 3
>>> import pandas as pd
>>> expr = pd.DataFrame({"day_of_year": list(range(365)), "value": list(range(365))}).to_expr()
>>> expr
<Expression size=365 dimensions={'day_of_year': 365} terms=365>
[0]: 0
[1]: 1
[2]: 2
"""

print_max_set_elements: int = 50
"Number of elements to show when printing a set to the console (additional elements are replaced with ...)"
"""
Maximum number of elements in a set to print.

Examples:
>>> pf.Config.print_max_set_elements = 5
>>> pf.Set(x=range(1000))
<Set size=1000 dimensions={'x': 1000}>
[0, 1, 2, 3, 4, …]
"""

enable_is_duplicated_expression_safety_check: bool = False
"""
Setting for internal testing purposes only.

When `True`, pyoframe checks that there are no bugs leading to duplicated terms in expressions.
"""

integer_tolerance: float = 1e-8
"""
For convenience, Pyoframe returns the solution of integer and binary variables as integers not floating point values.
To do so, Pyoframe must convert the solver-provided floating point values to integers. To avoid unexpected rounding errors,
Pyoframe uses this tolerance to check that the floating point result is an integer as expected. Overly tight tolerances can trigger
unexpected errors. Setting the tolerance to zero disables the check.
Tolerance for checking if a floating point value is an integer.

!!! info
For convenience, Pyoframe returns the solution of integer and binary variables as integers not floating point values.
To do so, Pyoframe must convert the solver-provided floating point values to integers. To avoid unexpected rounding errors,
Pyoframe uses this tolerance to check that the floating point result is an integer as expected. Overly tight tolerances can trigger
unexpected errors. Setting the tolerance to zero disables the check.
"""

float_to_str_precision: Optional[int] = 5
"""Number of decimal places to use when displaying mathematical expressions."""

print_uses_variable_names: bool = True
"""
Improve performance by not tracking the link between variable IDs and variable names.

If set to `False`, printed expression will use variable IDs instead of variable names
which might make debugging difficult.

!!! warning
This setting must be changed before instantiating a [Model][pyoframe.Model].

Examples:
>>> pf.Config.print_uses_variable_names = False
>>> m = pf.Model()
>>> m.my_var = pf.Variable()
>>> 2 * m.my_var
<Expression size=1 dimensions={} terms=1>
2 x1
"""

@classmethod
Expand Down Expand Up @@ -154,6 +260,8 @@ def to_poi(self):


class VType(Enum):
"""An enum to specify the variable type (continuous, binary, or integer)."""

CONTINUOUS = "continuous"
BINARY = "binary"
INTEGER = "integer"
Expand Down
Loading
Loading