-
Notifications
You must be signed in to change notification settings - Fork 2
README.md quick start paragraph #104
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
Changes from all commits
9a89243
cc9f076
ce84cf9
a779f79
0139720
677dcc0
2a47e45
b868b9f
c4af0c5
51150da
350b203
ec35965
0cc3d85
2fed4f0
1e15bac
94ae848
6249f2b
29ea4ed
2283d16
a79a31b
2771350
72ed08c
bcf24a5
292988f
27506a4
4f818e4
5793224
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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" | ||
| } | ||
| } |
| 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 |
| 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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() | ||
|
|
||
| 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is
quickstart.shpart 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__.pycould be avoided. Adding that file changes the nature of thesmlppackage from being a namespace to a proper package, which I would like to avoid while we have no official API.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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.shto bin directory and call itsmlp_quickstart, so there will be no overhead.There was a problem hiding this comment.
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.