Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
398 changes: 273 additions & 125 deletions METADATA_STORAGE_V3.md

Large diffs are not rendered by default.

2,802 changes: 2,122 additions & 680 deletions docs/api/api_reference.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion docs/conf_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@
# Allow overriding execution mode via environment variable (e.g. OMMX_NB_EXECUTION=force)
nb_execution_mode = os.environ.get("OMMX_NB_EXECUTION", "off")
nb_execution_timeout = 300
nb_execution_excludepatterns = ["release_note/ommx-1.*.md"]
nb_execution_excludepatterns = [
"release_note/ommx-1.*.md",
"release_note/ommx-2.*.md",
]
nb_execution_raise_on_error = True

# -- Options for HTML output -------------------------------------------------
Expand Down
38 changes: 35 additions & 3 deletions docs/en/release_note/ommx-3.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,38 @@ Python SDK 3.0.0 contains breaking API changes. A migration guide is available i

## Unreleased

### ⚠ `*_df` accessors are methods + `include=` filter + sidecar DataFrames ([#846](https://github.com/Jij-Inc/ommx/pull/846))

Every `*_df` accessor on `Instance` / `ParametricInstance` / `Solution` / `SampleSet` is now a regular method instead of a `#[getter]` property. Existing call sites need parentheses:

```python
# Before
df = solution.constraints_df

# After
df = solution.constraints_df()
```

The wide `*_df` methods take an `include` argument that gates the metadata / parameters column families. The default `include=("metadata", "parameters")` preserves the v2-equivalent wide shape:

```python
solution.decision_variables_df() # core + metadata + parameters
solution.decision_variables_df(include=[]) # core only
solution.decision_variables_df(include=["metadata"]) # core + metadata
solution.decision_variables_df(include=["parameters"]) # core + parameters
```

Six new long-format / id-indexed sidecar accessors read directly from the SoA metadata stores. `kind=` selects the constraint family (`"regular"` / `"indicator"` / `"one_hot"` / `"sos1"`, default `"regular"`):

- `constraint_metadata_df(kind=...)` — id-indexed (`name` / `subscripts` / `description`)
- `constraint_parameters_df(kind=...)` — long format (`{kind}_constraint_id` / `key` / `value`)
- `constraint_provenance_df(kind=...)` — long format (`{kind}_constraint_id` / `step` / `source_kind` / `source_id`)
- `constraint_removed_reasons_df(kind=...)` — long format (`{kind}_constraint_id` / `reason` / `key` / `value`)
- `variable_metadata_df()` — id-indexed
- `variable_parameters_df()` — long format

Sidecar index names are kind-qualified (`regular_constraint_id` / `indicator_constraint_id` / `one_hot_constraint_id` / `sos1_constraint_id` / `variable_id`) so accidental cross-id-space `df.join()` mistakes surface in `df.head()` and friends. Long-format `*_parameters_df` / `*_removed_reasons_df` rows are sorted by `(id, key)`, and empty long-format DataFrames keep their column schema instead of returning a column-less frame.

### ⚠ `to_bytes` / `from_bytes` removed from non-top-level types ([#845](https://github.com/Jij-Inc/ommx/pull/845))

Bytes serialization is removed from the following component-level types:
Expand Down Expand Up @@ -107,14 +139,14 @@ Accordingly, the legacy `ConstraintHints` / `OneHot` / `Sos1` classes, the `Inst

### ⚠ `removed_reason` column split into a separate table ([#796](https://github.com/Jij-Inc/ommx/pull/796))

In v2.5.1 {attr}`Solution.constraints_df <ommx.v1.Solution.constraints_df>` carried a `removed_reason` column. In v3.0.0a2 that column is split out into a separate {attr}`Solution.removed_reasons_df <ommx.v1.Solution.removed_reasons_df>` table, which you can join on if you need the previous shape. The same change applies to {class}`~ommx.v1.SampleSet`.
In v2.5.1 {meth}`Solution.constraints_df <ommx.v1.Solution.constraints_df>` carried a `removed_reason` column. In v3.0.0a2 that column is split out into a separate {meth}`Solution.removed_reasons_df <ommx.v1.Solution.removed_reasons_df>` table, which you can join on if you need the previous shape. The same change applies to {class}`~ommx.v1.SampleSet`.

```python
# Before (2.5.1)
df = solution.constraints_df # contains a 'removed_reason' column

# After (3.0.0a2)
df = solution.constraints_df.join(solution.removed_reasons_df)
# After (3.0.0a3 — `*_df` are now methods)
df = solution.constraints_df().join(solution.removed_reasons_df())
```

Corresponding `*_removed_reasons_df` accessors are also provided for Indicator, OneHot, and SOS1.
Expand Down
2 changes: 1 addition & 1 deletion docs/en/tutorial/share_in_ommx_artifact.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ instance = Instance.from_components(
solution = OMMXPySCIPOptAdapter.solve(instance)

# Analyze the optimal solution
df_vars = solution.decision_variables_df
df_vars = solution.decision_variables_df()
df = pd.DataFrame.from_dict(
{
"Item Number": df_vars.index,
Expand Down
10 changes: 5 additions & 5 deletions docs/en/tutorial/solve_with_ommx_adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,18 +143,18 @@ To do this, we use the properties implemented in the `ommx.v1.Solution` class.

### Analyzing the Optimal Solution

The `decision_variables_df` property returns a `pandas.DataFrame` object containing information on each variable, such as ID, type, name, and value:
The `decision_variables_df()` method returns a `pandas.DataFrame` object containing information on each variable, such as ID, type, name, and value:

```{code-cell} ipython3
solution.decision_variables_df
solution.decision_variables_df()
```

Using this `pandas.DataFrame` object, you can easily create a table in pandas that shows, for example, "whether to put items in the knapsack":

```{code-cell} ipython3
import pandas as pd

df = solution.decision_variables_df
df = solution.decision_variables_df()
pd.DataFrame.from_dict(
{
"Item number": df.index,
Expand All @@ -178,10 +178,10 @@ assert np.isclose(solution.objective, expected)

### Analyzing Constraints

The `constraints_df` property returns a `pandas.DataFrame` object that includes details about each constraint's equality or inequality, its left-hand-side value (`"value"`), name, and more:
The `constraints_df()` method returns a `pandas.DataFrame` object that includes details about each constraint's equality or inequality, its left-hand-side value (`"value"`), name, and more:

```{code-cell} ipython3
solution.constraints_df
solution.constraints_df()
```

Specifically, the `"value"` is helpful for understanding how much slack remains in each constraint. In this case, item 0 has weight $w_0 = 11$, item 3 has weight $w_3 = 35$, and the knapsack's capacity $W$ is $47$. Therefore, for the weight constraint
Expand Down
2 changes: 1 addition & 1 deletion docs/en/tutorial/switching_adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ It would be convenient to concatenate the `pandas.DataFrame` obtained with `deci
import pandas

decision_variables = pandas.concat([
solution.decision_variables_df.assign(solver=solver)
solution.decision_variables_df().assign(solver=solver)
for solver, solution in solutions.items()
])
decision_variables
Expand Down
6 changes: 3 additions & 3 deletions docs/en/tutorial/tsp_sampling_with_openjij_adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,14 @@ To view the feasibility for each constraint, use the `summary_with_constraints`
sample_set.summary_with_constraints
```

For more detailed information, you can use the `SampleSet.decision_variables` and `SampleSet.constraints` properties.
For more detailed information, you can use the `SampleSet.decision_variables_df()` and `SampleSet.constraints_df()` methods.

```{code-cell} ipython3
sample_set.decision_variables_df.head(2)
sample_set.decision_variables_df().head(2)
```

```{code-cell} ipython3
sample_set.constraints_df.head(2)
sample_set.constraints_df().head(2)
```

To obtain the samples, use the `SampleSet.extract_decision_variables` method. This interprets the samples using the `name` and `subscripts` registered when creating `ommx.v1.DecisionVariables`. For example, to get the value of the decision variable named `x` with `sample_id=1`, use the following to obtain it in the form of `dict[subscripts, value]`.
Expand Down
6 changes: 3 additions & 3 deletions docs/en/user_guide/capability_model.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,9 @@ The original special constraints are not discarded; they are kept as "removed" e

| Original type | Removed dict | DataFrame |
|---|---|---|
| OneHotConstraint | {attr}`~ommx.v1.Instance.removed_one_hot_constraints` | {attr}`~ommx.v1.Instance.removed_one_hot_constraints_df` |
| Sos1Constraint | {attr}`~ommx.v1.Instance.removed_sos1_constraints` | {attr}`~ommx.v1.Instance.removed_sos1_constraints_df` |
| IndicatorConstraint | {attr}`~ommx.v1.Instance.removed_indicator_constraints` | {attr}`~ommx.v1.Instance.removed_indicator_constraints_df` |
| OneHotConstraint | {attr}`~ommx.v1.Instance.removed_one_hot_constraints` | {meth}`~ommx.v1.Instance.removed_one_hot_constraints_df` |
| Sos1Constraint | {attr}`~ommx.v1.Instance.removed_sos1_constraints` | {meth}`~ommx.v1.Instance.removed_sos1_constraints_df` |
| IndicatorConstraint | {attr}`~ommx.v1.Instance.removed_indicator_constraints` | {meth}`~ommx.v1.Instance.removed_indicator_constraints_df` |

Each entry ({class}`~ommx.v1.RemovedOneHotConstraint` / {class}`~ommx.v1.RemovedSos1Constraint` / {class}`~ommx.v1.RemovedIndicatorConstraint`) records a `removed_reason` string (for example, `"ommx.Instance.convert_one_hot_to_constraint"`) and stores the generated regular-constraint IDs in `removed_reason_parameters`. The key name and shape differ by constraint type:

Expand Down
4 changes: 2 additions & 2 deletions docs/en/user_guide/instance.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ instance.sense == Instance.MAXIMIZE
Decision variables and constraints can be obtained in the form of [`pandas.DataFrame`](https://pandas.pydata.org/pandas-docs/stable/reference/frame.html).

```{code-cell} ipython3
instance.decision_variables_df
instance.decision_variables_df()
```

First, `kind`, `lower`, and `upper` are essential information for the mathematical model.
Expand Down Expand Up @@ -95,7 +95,7 @@ print(f"{x1.id=}, {x1.name=}")
Next, let's look at the constraints.

```{code-cell} ipython3
instance.constraints_df
instance.constraints_df()
```

In OMMX, constraints are also managed by ID, and this ID is independent of the decision variable ID. The ID is assigned when a constraint is attached to an `Instance`: the key you use in the `constraints` dictionary passed to [`Instance.from_components`](https://jij-inc.github.io/ommx/python/ommx/autoapi/ommx/v1/index.html#ommx.v1.Instance.from_components) becomes the constraint ID.
Expand Down
4 changes: 2 additions & 2 deletions docs/en/user_guide/parametric_instance.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ parametric_instance = ParametricInstance.from_components(
)
```

Like `ommx.v1.Instance`, you can view the decision variables and constraints as DataFrames through the `decision_variables_df` and `constraints_df` properties. In addition, `ommx.v1.ParametricInstance` has a `parameters_df` property for viewing parameter information in a DataFrame.
Like `ommx.v1.Instance`, you can view the decision variables and constraints as DataFrames through the `decision_variables_df()` and `constraints_df()` methods. In addition, `ommx.v1.ParametricInstance` has a `parameters_df()` method for viewing parameter information in a DataFrame.

```{code-cell} ipython3
parametric_instance.parameters_df
parametric_instance.parameters_df()
```

Next, let’s assign specific values to the parameters. Use `ParametricInstance.with_parameters`, which takes a dictionary mapping each `ommx.v1.Parameter` ID to its corresponding value.
Expand Down
4 changes: 2 additions & 2 deletions docs/en/user_guide/sample_set.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ solution = sample_set.get(sample_id=0)
assert isinstance(solution, Solution)

print(f"{solution.objective=}")
solution.decision_variables_df
solution.decision_variables_df()
```

Retrieving the best solution
Expand All @@ -108,7 +108,7 @@ Retrieving the best solution
solution = sample_set.best_feasible

print(f"{solution.objective=}")
solution.decision_variables_df
solution.decision_variables_df()
```

Of course, if the problem is a minimization, the sample with the smallest objective value will be returned. If no feasible samples exist, an error will be raised.
Expand Down
4 changes: 2 additions & 2 deletions docs/en/user_guide/solution.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ solution = instance.evaluate({1: 1, 2: 0}) # x=1, y=0
The generated `ommx.v1.Solution` inherits most of the information from the `ommx.v1.Instance`. Let's first look at the decision variables.

```{code-cell} ipython3
solution.decision_variables_df
solution.decision_variables_df()
```

In addition to the required attributes—ID, `kind`, `lower`, and `upper`—it also inherits metadata such as `name`. Additionally, the `value` assigned in `evaluate` is stored. Similarly, the evaluation value is added to the constraints as `value`.

```{code-cell} ipython3
solution.constraints_df
solution.constraints_df()
```

The `objective` property contains the value of the objective function, and the `feasible` property contains whether the constraints are satisfied.
Expand Down
12 changes: 6 additions & 6 deletions docs/en/user_guide/special_constraints.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,19 +180,19 @@ The {class}`~ommx.v1.Solution` or {class}`~ommx.v1.SampleSet` obtained after sol

| Constraint type | Accessor (on `Solution`) |
|---|---|
| Regular | {attr}`~ommx.v1.Solution.constraints_df` |
| Indicator | {attr}`~ommx.v1.Solution.indicator_constraints_df` |
| OneHot | {attr}`~ommx.v1.Solution.one_hot_constraints_df` |
| SOS1 | {attr}`~ommx.v1.Solution.sos1_constraints_df` |
| Regular | {meth}`~ommx.v1.Solution.constraints_df` |
| Indicator | {meth}`~ommx.v1.Solution.indicator_constraints_df` |
| OneHot | {meth}`~ommx.v1.Solution.one_hot_constraints_df` |
| SOS1 | {meth}`~ommx.v1.Solution.sos1_constraints_df` |

The Indicator DataFrame includes an `indicator_active` column that disambiguates "the indicator was OFF (constraint trivially satisfied)" from "the indicator was ON and the constraint was actually satisfied". Indicator constraints do not carry a dual variable — a dual value is not well-defined for a conditional constraint — so `dual_variable` is omitted.

### removed_reasons_df separation

For regular constraints, `removed_reason` is no longer a column of {attr}`~ommx.v1.Solution.constraints_df`. It lives in {attr}`~ommx.v1.Solution.removed_reasons_df` as a separate table, which you can join as needed:
For regular constraints, `removed_reason` is no longer a column of {meth}`~ommx.v1.Solution.constraints_df`. It lives in {meth}`~ommx.v1.Solution.removed_reasons_df` as a separate table, which you can join as needed:

```python
df = solution.constraints_df.join(solution.removed_reasons_df)
df = solution.constraints_df().join(solution.removed_reasons_df())
```

The same split applies to Indicator, OneHot, and SOS1: each has its own `indicator_removed_reasons_df` / `one_hot_removed_reasons_df` / `sos1_removed_reasons_df` on both {class}`~ommx.v1.Solution` and {class}`~ommx.v1.SampleSet`.
Expand Down
38 changes: 35 additions & 3 deletions docs/ja/release_note/ommx-3.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,38 @@ Python SDK 3.0.0にはAPIの破壊的な変更が含まれます。マイグレ

## Unreleased

### ⚠ `*_df` アクセサがメソッドに変更 + `include=` 追加 + Sidecar DataFrame ([#846](https://github.com/Jij-Inc/ommx/pull/846))

`Instance` / `ParametricInstance` / `Solution` / `SampleSet` のすべての `*_df` アクセサを `#[getter]` プロパティから通常のメソッドに変更しました。プロパティアクセスからメソッド呼び出しに移行する必要があります:

```python
# Before
df = solution.constraints_df

# After
df = solution.constraints_df()
```

ワイドな `*_df` メソッドには `include` 引数が追加され、メタデータ系・パラメータ系のカラムをそれぞれ ON/OFF できます。デフォルトの `include=("metadata", "parameters")` は v2 互換のワイド形を維持します:

```python
solution.decision_variables_df() # core + metadata + parameters
solution.decision_variables_df(include=[]) # core only
solution.decision_variables_df(include=["metadata"]) # core + metadata
solution.decision_variables_df(include=["parameters"]) # core + parameters
```

加えて、SoA メタデータストアを直接読む 6 種類の long-format / id-indexed sidecar アクセサが追加されました。`kind=` で対象の制約ファミリーを切り替えます (`"regular"` / `"indicator"` / `"one_hot"` / `"sos1"`、デフォルト `"regular"`):

- `constraint_metadata_df(kind=...)` — id-indexed (`name` / `subscripts` / `description`)
- `constraint_parameters_df(kind=...)` — long format (`{kind}_constraint_id` / `key` / `value`)
- `constraint_provenance_df(kind=...)` — long format (`{kind}_constraint_id` / `step` / `source_kind` / `source_id`)
- `constraint_removed_reasons_df(kind=...)` — long format (`{kind}_constraint_id` / `reason` / `key` / `value`)
- `variable_metadata_df()` — id-indexed
- `variable_parameters_df()` — long format

Sidecar の index 名はファミリーごとに qualified (`regular_constraint_id` / `indicator_constraint_id` / `one_hot_constraint_id` / `sos1_constraint_id` / `variable_id`) になっており、別 ID 空間どうしを誤って `df.join()` した場合に `df.head()` 等で気づきやすくなっています。`*_parameters_df` / `*_removed_reasons_df` の行は `(id, key)` 順にソート済み、空の long-format DataFrame もスキーマ列だけ持つ形で返ります。

### ⚠ 部品型から `to_bytes` / `from_bytes` を削除 ([#845](https://github.com/Jij-Inc/ommx/pull/845))

以下の部品型からバイト列シリアライズを削除しました:
Expand Down Expand Up @@ -107,14 +139,14 @@ Instance.from_components(..., constraints={5: c}, ...)

### ⚠ `removed_reason` カラムを別テーブルに分離 ([#796](https://github.com/Jij-Inc/ommx/pull/796))

v2.5.1 までは {attr}`Solution.constraints_df <ommx.v1.Solution.constraints_df>` に `removed_reason` カラムが含まれていましたが、v3.0.0a2 ではこれを {attr}`Solution.removed_reasons_df <ommx.v1.Solution.removed_reasons_df>` という別テーブルに分離しました。従来の形が必要な場合は join してください。同じ変更が {class}`~ommx.v1.SampleSet` にも適用されています。
v2.5.1 までは {meth}`Solution.constraints_df <ommx.v1.Solution.constraints_df>` に `removed_reason` カラムが含まれていましたが、v3.0.0a2 ではこれを {meth}`Solution.removed_reasons_df <ommx.v1.Solution.removed_reasons_df>` という別テーブルに分離しました。従来の形が必要な場合は join してください。同じ変更が {class}`~ommx.v1.SampleSet` にも適用されています。

```python
# Before (2.5.1)
df = solution.constraints_df # 'removed_reason' カラムを含む

# After (3.0.0a2)
df = solution.constraints_df.join(solution.removed_reasons_df)
# After (3.0.0a3 — `*_df` はメソッドになりました)
df = solution.constraints_df().join(solution.removed_reasons_df())
```

Indicator / OneHot / SOS1 それぞれに対応する `*_removed_reasons_df` も提供されています。
Expand Down
2 changes: 1 addition & 1 deletion docs/ja/tutorial/share_in_ommx_artifact.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ instance = Instance.from_components(
solution = OMMXPySCIPOptAdapter.solve(instance)

# 最適解の分析をする
df_vars = solution.decision_variables_df
df_vars = solution.decision_variables_df()
df = pd.DataFrame.from_dict(
{
"アイテムの番号": df_vars.index,
Expand Down
10 changes: 5 additions & 5 deletions docs/ja/tutorial/solve_with_ommx_adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,18 +144,18 @@ solution = OMMXPySCIPOptAdapter.solve(instance)

### 最適解の分析

`decision_variables` プロパティは、決定変数のID、種類、名前、値などの情報を含む `pandas.DataFrame` オブジェクトを返します:
`decision_variables_df()` メソッドは、決定変数のID、種類、名前、値などの情報を含む `pandas.DataFrame` オブジェクトを返します:

```{code-cell} ipython3
solution.decision_variables_df
solution.decision_variables_df()
```

この `pandas.DataFrame` オブジェクトを使うことで、例えば「アイテムをナップサックに入れるかどうか」という判断をまとめた表を pandas で簡単に作成できます:

```{code-cell} ipython3
import pandas as pd

df = solution.decision_variables_df
df = solution.decision_variables_df()
pd.DataFrame.from_dict(
{
"アイテムの番号": df.index,
Expand All @@ -179,10 +179,10 @@ assert np.isclose(solution.objective, expected)

### 制約条件の分析

`constraints` プロパティは、制約条件の等号不等号、左辺の値 (`"value"`)、名前などの情報を含む `pandas.DataFrame` オブジェクトを返します:
`constraints_df()` メソッドは、制約条件の等号不等号、左辺の値 (`"value"`)、名前などの情報を含む `pandas.DataFrame` オブジェクトを返します:

```{code-cell} ipython3
solution.constraints_df
solution.constraints_df()
```

特に `"value"` は制約条件にどの程度の余裕があるのかを知るために便利です。今回のケースでは、0番目のアイテム $w_0$ の重さが `11`、3番目のアイテムの重さ $w_3$ が `35` であり、ナップサックの耐荷重 $W$ は `47` なので、重量制約
Expand Down
Loading
Loading