Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9a89243
Quickstart initial commit
mdmitry1 Apr 3, 2026
cc9f076
Adding image for README.md
mdmitry1 Apr 3, 2026
ce84cf9
1. Added quickstart to the README.md
mdmitry1 Apr 3, 2026
a779f79
Fixed typo
mdmitry1 Apr 3, 2026
0139720
Added quickstart to the smlptech package
mdmitry1 Apr 4, 2026
677dcc0
Fixed copy-paste error
mdmitry1 Apr 4, 2026
2a47e45
Changes after the review
mdmitry1 Apr 7, 2026
b868b9f
Improved test case description
mdmitry1 Apr 7, 2026
c4af0c5
Added missing line break
mdmitry1 Apr 7, 2026
51150da
Sync with master after PR#105 merge
mdmitry1 Apr 18, 2026
350b203
Merge remote-tracking branch 'remotes/origin/master' into smlp_quicks…
mdmitry1 Apr 18, 2026
ec35965
Merge after PR #103 merge into main branch
mdmitry1 Apr 19, 2026
0cc3d85
Merge remote-tracking branch 'remotes/origin/master' into smlp_quicks…
mdmitry1 Apr 19, 2026
2fed4f0
Synch with master
mdmitry1 Apr 21, 2026
1e15bac
Merge remote-tracking branch 'remotes/origin/master' into smlp_quicks…
mdmitry1 Apr 21, 2026
94ae848
Synch with main branch
mdmitry1 Apr 22, 2026
6249f2b
Merge remote-tracking branch 'remotes/origin/master' into smlp_quicks…
mdmitry1 Apr 22, 2026
29ea4ed
Synch with master
mdmitry1 Apr 22, 2026
2283d16
Merge remote-tracking branch 'remotes/origin/master' into smlp_quicks…
mdmitry1 Apr 22, 2026
a79a31b
Modified after the review
mdmitry1 Apr 23, 2026
2771350
Fixing typo
mdmitry1 Apr 23, 2026
72ed08c
Improved formatting
mdmitry1 Apr 23, 2026
bcf24a5
Fixed formatting in quickstart testcase details
mdmitry1 Apr 23, 2026
292988f
Fixed formatting in quickstart testcase details
mdmitry1 Apr 23, 2026
27506a4
Merge branch 'smlp_quickstart' of ssh://github.com/SMLP-Systems/smlp …
mdmitry1 Apr 23, 2026
4f818e4
Added more line breaks to improve readability on pypi.org
mdmitry1 Apr 23, 2026
5793224
Removed <br> in markdown code block
mdmitry1 Apr 23, 2026
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
99 changes: 99 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,105 @@ tests/install/test_container_install mdmitry1/python311-dev
Installation instructions for Ubuntu-22.04 can be followed using `homebrew` in place of `apt`
</details>

## Quickstart

### Problem: find minimal distance between point (2,1) and unit circle<br>

<p align="left">
<img src="https://raw.githubusercontent.com/SMLP-Systems/smlp/smlp_quickstart/misc/minimal_distance.png" alt="Minimal Distance Problem" class="center" width="500" height="400"></p>

Analytical solution for this problem:<br>
`
f(x*) = 6 - 2√5 ≈ 1.527864`, where `x* = (2/√5,1/√5) ≈ (0.894427, 0.447214)`
<br><br>
Solution: see `bash` script [quickstart.sh](https://raw.githubusercontent.com/SMLP-Systems/smlp/smlp_quickstart/quickstart/quickstart.sh)<br><br>
The script has 2 steps<br>
- Step 1: Create input dataset and visualize the problem<br>
- Step 2: Run SMLP<br>
SMLP creates polynomial model and finds approximate solution<br>
SMLP results
`f(x*) = 1.527865, x* = (0.894531, 0.447004)`<br>
are within 0.05% accuracy for `f(x*)` and `x*`

Running the script:
```bash
smlp_package_path=$(python3.11 -c 'import smlp; from os.path import dirname; print(dirname(smlp.__file__))')
$smlp_package_path/quickstart/quickstart.sh
```
Comment on lines +245 to +248

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is quickstart.sh part of the installed wheel and therefore this non-intuitive dance for obtaining its path is necessary? The script is already linked above. In case the additional 3 files are required for it to run, would it be possible to zip them and provide a link to archive for users to download and unpack instead?

That way, also adding __init__.py could be avoided. Adding that file changes the nature of the smlp package from being a namespace to a proper package, which I would like to avoid while we have no official API.

@mdmitry1 mdmitry1 Apr 22, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that for the user will be much easier to type this line than download from somewhere else and unpack.
This feature has been requested by Konstantin - let him to decide.
We can also put quickstart.sh to bin directory and call it smlp_quickstart, so there will be no overhead.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To my understanding, the point of this "quickstart" exercise is to give users something they can play with. Scripts, data, all of it. If the script is "installed" instead of just being a local file, users are much less likely to play with the script, to try new options or to see what's going on because it is installed and thus part of "the system" and users are trained to not edit their system files directly.

<br>

<details>
<summary>Test case description</summary><br>

**1.** *constraint_dora.json* - spec in json format<br>

```
{
"version": "1.2",
"variables": [
{"label":"X1", "interface":"knob", "type":"real", "range":[-1.5,2.5], "rad-abs": 0.0},
{"label":"X2", "interface":"knob", "type":"real", "range":[-1.5,2.0], "rad-abs": 0.0},
{"label":"Y1", "interface":"output", "type":"real"}
],
"alpha": "X1*X1+X2*X2<=1",
"objectives": {"objective1": "-Y1"}
}
```

<u>Legend:</u><br>

```
X1 - first controllable variable
X2 - second controlllable variable
Y1 - output function
rad-abs - sensitivity radius.
Zero radius means that solution sensitivity check is skipped
alpha - constraint depending on controllable variables
objective1 - optimization goal
```

<br>

**2.** SMLP command line arguments<br>

```
-data ${name}.csv.gz # input CSV dataset
-spec ${script_path}/${name_lc}.json # JSON spec file
-pref ${name} # output file prefix
-mode optimize # operation mode
-model poly_sklearn # model type
-epsilon 0.0000005 # convergence threshold
```

</details>

### Problem modification in the user area

- Step 1: Copy the problem to the current directory and enter problem work area<br>
```bash
smlp_package_path=$(python3.11 -c 'import smlp; from os.path import dirname; print(dirname(smlp.__file__))')
\cp -rp $smlp_package_path/quickstart .
cd quickstart
```
- Step 2: As an example, change constraint in order to get solution in rational numbers<br>
Let's change circle radius to 2/√5, so squared radius will be 4/5<br>
In order to do this, edit `constraint_dora.json` file and change right side of the inequality to be 4/5:<br>
`"alpha": "X1*X1+X2*X2<=4/5",`<br><br>
[Analytical solution](https://www.wolframalpha.com/input?i=Minimize%3A+f%28x1%2C+x2%29+%3D+%28x1+-+2%29%5E2+%2B+%28x2+-+1%29%5E2+subject+to+x1%5E2+%2B+x2%5E2+-+4%2F5+%3C%3D+0) for modified problem:<br>
`
f(x*) = 9/5 = 1.8`, where `x* = (4/5,2/5) = (0.8, 0.4)`<br><br>
- Step 3: Run the script from current directory
```bash
./quickstart.sh
```
Expected SMLP results are within 0.03% accuracy for `f(x*)` and `x*`:
```bash
Working directory: <current_directory_realpath>/quickstart/Constraint_dora_results_<timestamp>
X1 = 0.800048828125
X2 = 0.3999021053314209
Y1 = 1.8000002980730385
```

## [Tutorial](https://github.com/SMLP-Systems/smlp/tree/master/tutorial)

- Black-box optimization Eggholder Function
Expand Down
Binary file added misc/minimal_distance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions quickstart/constraint_dora.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "1.2",
"variables": [
{"label":"X1", "interface":"knob", "type":"real", "range":[-1.5,2.5], "rad-abs": 0.0},
{"label":"X2", "interface":"knob", "type":"real", "range":[-1.5,2.0], "rad-abs": 0.0},
{"label":"Y1", "interface":"output", "type":"real"}
],
"alpha": "X1*X1+X2*X2<=1",
"objectives": {
"objective1": "-Y1"
}
}
85 changes: 85 additions & 0 deletions quickstart/constraint_dora_dataset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env python3.11
"""
Constrained Optimization Example using SMLP
Classic Lagrange Multiplier Problem (appears in many optimization textbooks)

Problem:
Minimize: f(x1, x2) = (x1 - 2)^2 + (x2 - 1)^2
Subject to: x1^2 + x2^2 - 1 <= 0 (inside unit circle)

Geometrically: Find the point on the unit circle closest to (2, 1)

Analytical Solution (using Lagrange multipliers):
Setting ∇f = λ∇g where g(x1,x2) = x1² + x2² - 1:
- 2(x1-2) = 2λx1 → x1 = 2/(1+λ)
- 2(x2-1) = 2λx2 → x2 = 1/(1+λ)
- Substituting into constraint: 5 = (1+λ)²
- Solving: λ = √5 - 1

Expected Result:
Optimal point: x1 = 2/√5 ≈ 0.894427, x2 = 1/√5 ≈ 0.447214
Minimum value: f = (√5 - 1)² ≈ 1.527864

Reference: Wolfram Alpha
https://www.wolframalpha.com/input?i=Minimize%3A+f%28x1%2C+x2%29+%3D+%28x1+-+2%29%5E2+%2B+%28x2+-+1%29%5E2+subject+to+x1%5E2+%2B+x2%5E2+-+1+%3C%3D+0
"""

from sys import argv
from numpy import linspace, meshgrid, cos, sin, pi, sqrt, inf
from pandas import read_csv, concat
from gzip import open as gzopen
from matplotlib import pyplot as plt

def main():
# Initial guess
rng = range(0, 1000)
x1_start, x1_stop = (-1.5, 2.5)
x2_start, x2_stop = (-1.5, 2.0)
x1 = linspace(x1_start, x1_stop, rng.stop)
x2 = linspace(x2_start, x2_stop, rng.stop)
X1, X2 = meshgrid(x1, x2)
Z = (X1 - 2)**2 + (X2 - 1)**2
with gzopen('Constraint_dora.csv.gz',"wt") as ds:
ds.write("X1,X2,Y1\n")
[[ds.write(f"{X1[i][j]},{X2[i][j]},{Z[i][j]}\n") for j in rng] for i in rng]

# Visualization
fig, ax = plt.subplots(figsize=(10, 8))

# Plot contours of objective function
contours = ax.contour(X1, X2, Z, levels=20, cmap='viridis', alpha=0.6)
ax.clabel(contours, inline=True, fontsize=8)

# Plot constraint boundary (unit circle)
theta = linspace(0, 2*pi, 100)
circle_x = cos(theta)
circle_y = sin(theta)
ax.plot(circle_x, circle_y, 'r-', linewidth=2, label='Constraint boundary')
ax.fill(circle_x, circle_y, alpha=0.1, color='red', label='Feasible region')

# Plot unconstrained optimum
ax.plot(2, 1, 'bs', markersize=12, label='Unconstrained optimum (2, 1)')

ax.set_xlabel('x1', fontsize=12)
ax.set_ylabel('x2', fontsize=12)
ax.set_title('Constrained Optimization: Minimize f(x1,x2) subject to x1² + x2² ≤ 1',
fontsize=14)
# Plot constrained optimum
ax.plot(2/sqrt(5), 1/sqrt(5), 'go', markersize=12, label=f'Constrained optimum ({2/sqrt(5):.3f}, {1/sqrt(5):.3f})')
ax.legend(loc='upper right')
ax.grid(True, alpha=0.3)
ax.axis('equal')
ax.set_ylim(-1.5, 2.0)

plt.tight_layout()
timeout = inf
if len(argv) > 2:
if '-timeout' == argv[1]:
timeout = int(argv[2])
if not inf == timeout:
timer = fig.canvas.new_timer(interval=timeout*1000, callbacks=[(plt.close, [], {})])
timer.start()
plt.show()

if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
X1 = 0.89453125
X2 = 0.4470043182373047
Y1 = 1.5278653812779421
33 changes: 33 additions & 0 deletions quickstart/quickstart.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
script_path="$(dirname "$(realpath "$0")")"
name=Constraint_dora
if [[ $# -gt 0 ]]; then
if [[ "-clean" == "$1" ]]; then
rm -rf ${name}_results_* 2>/dev/null
exit 0
fi
fi
results_dir=${name}_results_$(date +%s)
rm -rf $results_dir 2>/dev/null
mkdir -p $results_dir
echo "Working directory: $(realpath $results_dir)"
cd $results_dir
log=${name}.log
dataset=${name}.csv.gz
name_lc="$(echo "$name" | tr '[:upper:]' '[:lower:]')"
"${script_path}/${name_lc}_dataset.py" #Create dataset and visualize the problem
results=${name}_poly_optimization_results.txt
rm -f "$results" 2>/dev/null
smlp_args=(
-data ${name}.csv.gz # input CSV dataset
-spec ${script_path}/${name_lc}.json # JSON spec file
-pref ${name} # output file prefix
-mode optimize # operation mode
-model poly_sklearn # model type
-epsilon 0.0000005 # convergence threshold
)

smlp "${smlp_args[@]}" >"$log" 2>&1
for var in X1 X2 Y1; do
echo "$var = $(jq ".${var}.value_in_config" ${name}_${name}_optimization_results.json)" 2>&1 | tee -a "$results"
done
42 changes: 23 additions & 19 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -780,25 +780,29 @@ def run(self):
shutil.copytree(str(installed_pkg), str(dest))
print(f"[smlp build] smlp extension copied to wheel at {dest}")

# 5. Copy Python source from src/smlp_py into smlp/smlp_py inside the wheel
smlp_py_src = REPO_ROOT / "src" / "smlp_py"
if smlp_py_src.is_dir():
smlp_py_dest = dest / "smlp_py" # dest is already smlp/
if smlp_py_dest.exists():
shutil.rmtree(smlp_py_dest)
shutil.copytree(str(smlp_py_src), str(smlp_py_dest))
print(f"[smlp build] smlp_py source copied to wheel at {smlp_py_dest}")
else:
print(f"[smlp build] WARNING: src/smlp_py not found at {smlp_py_src}, skipping.")

# 6. Copy src/run_smlp.py into smlp/ inside the wheel
run_smlp_src = REPO_ROOT / "src" / "run_smlp.py"
if run_smlp_src.is_file():
shutil.copy2(str(run_smlp_src), str(dest / "run_smlp.py"))
print(f"[smlp build] run_smlp.py copied to wheel at {dest / 'run_smlp.py'}")
else:
print(f"[smlp build] WARNING: src/run_smlp.py not found at {run_smlp_src}, skipping.")

# 5. Copy Python sources from src/ into smlp/ inside the wheel
sources = [
(REPO_ROOT / "src" / "smlp_py", dest / "smlp_py", "dir"),
(REPO_ROOT / "src" / "__init__.py", dest / "__init__.py", "file"),
(REPO_ROOT / "src" / "run_smlp.py", dest / "run_smlp.py", "file"),
(REPO_ROOT / "quickstart", dest / "quickstart", "dir"),
]
for src, dst, kind in sources:
copy_success = False
if kind == "dir":
if src.is_dir():
if dst.exists():
shutil.rmtree(str(dst))
shutil.copytree(str(src), str(dst))
copy_success = True
else:
if src.is_file():
shutil.copy2(str(src), str(dst))
copy_success = True
if copy_success:
print(f"[smlp build] {src} copied to wheel at {dst}")
else:
print(f"[smlp build] WARNING: source is not found at {src}, skipping.")
Comment on lines +802 to +805

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize that this change to setup.py generalizes the existing behaviour, but I'm wondering: Why would it be OK for the setup.py script to ignore copying non-existent but specified sources? This should be an error. If you concur, I'll open a separate issue about it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it simplifies debug - partial results are considered better alternative than no results on the initial project stages. I'm running regression after build anyway. Therefore I prefer to make it an issue. In order to fix it properly, we should have failure for production versions and warning for release candidates.


# ---------------------------------------------------------------------------
# setup()
Expand Down
2 changes: 2 additions & 0 deletions src/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: Apache-2.0
# This file is part of smlp.