gsimplex 0.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gsimplex-0.0.2/PKG-INFO +25 -0
- gsimplex-0.0.2/README.md +7 -0
- gsimplex-0.0.2/pyproject.toml +44 -0
- gsimplex-0.0.2/setup.cfg +4 -0
- gsimplex-0.0.2/src/gsimplex/__init__.py +1 -0
- gsimplex-0.0.2/src/gsimplex/benchmarks/downloader.py +76 -0
- gsimplex-0.0.2/src/gsimplex/benchmarks/netlib.py +78 -0
- gsimplex-0.0.2/src/gsimplex/benchmarks/netlib_emps.py +805 -0
- gsimplex-0.0.2/src/gsimplex/benchmarks/plato.py +55 -0
- gsimplex-0.0.2/src/gsimplex/demo.py +54 -0
- gsimplex-0.0.2/src/gsimplex/main.py +37 -0
- gsimplex-0.0.2/src/gsimplex/problem.py +46 -0
- gsimplex-0.0.2/src/gsimplex/solution.py +25 -0
- gsimplex-0.0.2/src/gsimplex/solvers/__init__.py +1 -0
- gsimplex-0.0.2/src/gsimplex/solvers/criss_cross.py +13 -0
- gsimplex-0.0.2/src/gsimplex/solvers/dual_simplex.py +104 -0
- gsimplex-0.0.2/src/gsimplex/solvers/gap_simplex.py +74 -0
- gsimplex-0.0.2/src/gsimplex/solvers/iterative_solver.py +20 -0
- gsimplex-0.0.2/src/gsimplex/solvers/primal_simplex.py +136 -0
- gsimplex-0.0.2/src/gsimplex/solvers/simplex_interface.py +15 -0
- gsimplex-0.0.2/src/gsimplex/solvers/solver_interface.py +27 -0
- gsimplex-0.0.2/src/gsimplex/tools/extractor.py +37 -0
- gsimplex-0.0.2/src/gsimplex/tools/parser.py +45 -0
- gsimplex-0.0.2/src/gsimplex/vertex.py +96 -0
- gsimplex-0.0.2/src/gsimplex.egg-info/PKG-INFO +25 -0
- gsimplex-0.0.2/src/gsimplex.egg-info/SOURCES.txt +30 -0
- gsimplex-0.0.2/src/gsimplex.egg-info/dependency_links.txt +1 -0
- gsimplex-0.0.2/src/gsimplex.egg-info/entry_points.txt +6 -0
- gsimplex-0.0.2/src/gsimplex.egg-info/requires.txt +6 -0
- gsimplex-0.0.2/src/gsimplex.egg-info/top_level.txt +1 -0
- gsimplex-0.0.2/tests/test_linear_programming.py +46 -0
- gsimplex-0.0.2/tests/test_simple.py +88 -0
gsimplex-0.0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gsimplex
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Implementation of simplex algorithm contrelled by the primal-dual gap
|
|
5
|
+
Author-email: Riccardo Ciucci <riccardo@ciucci.dev>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Richie314/GapControlledSimplex
|
|
8
|
+
Project-URL: Issues, https://github.com/Richie314/GapControlledSimplex/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.12
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: numpy>=2.2.0
|
|
14
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
15
|
+
Requires-Dist: pulp>=3.1.0
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest; extra == "dev"
|
|
18
|
+
|
|
19
|
+
# Gap controlled Simplex
|
|
20
|
+
|
|
21
|
+
[](https://github.com/Richie314/GapControlledSimplex/actions/workflows/pypi.yml)
|
|
22
|
+
|
|
23
|
+

|
|
24
|
+
|
|
25
|
+
## Download test problems
|
gsimplex-0.0.2/README.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Gap controlled Simplex
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Richie314/GapControlledSimplex/actions/workflows/pypi.yml)
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Download test problems
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = [ "setuptools >= 80.0.1", "wheel" ]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "gsimplex"
|
|
7
|
+
version = "0.0.2"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Riccardo Ciucci", email="riccardo@ciucci.dev" },
|
|
10
|
+
]
|
|
11
|
+
description = "Implementation of simplex algorithm contrelled by the primal-dual gap"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.12"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
]
|
|
18
|
+
license = "MIT"
|
|
19
|
+
license-files = ["LICEN[CS]E*"]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"numpy >= 2.2.0",
|
|
22
|
+
"aiohttp >= 3.9.0",
|
|
23
|
+
"pulp >= 3.1.0",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.scripts]
|
|
27
|
+
gsimplex = "gsimplex.main:__main"
|
|
28
|
+
gsimplex-demo = "gsimplex.demo:demo"
|
|
29
|
+
gsimplex-download-netlib = "gsimplex.benchmarks.netlib:main"
|
|
30
|
+
gsimplex-emps = "gsimplex.benchmarks.netlib_emps:__main"
|
|
31
|
+
gsimplex-download-plato = "gsimplex.benchmarks.plato:main"
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/Richie314/GapControlledSimplex"
|
|
35
|
+
Issues = "https://github.com/Richie314/GapControlledSimplex/issues"
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
dev = ["pytest"]
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.packages.find]
|
|
41
|
+
where = ["src"]
|
|
42
|
+
|
|
43
|
+
[tool.setuptools]
|
|
44
|
+
include-package-data = true
|
gsimplex-0.0.2/setup.cfg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Simplex library
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import aiohttp
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Dict, List, Tuple, Optional
|
|
5
|
+
|
|
6
|
+
class Downloader:
|
|
7
|
+
def __init__(self, benchmark_dir: Optional[str] = None, quiet: bool = False):
|
|
8
|
+
self._quiet = quiet
|
|
9
|
+
if benchmark_dir is None:
|
|
10
|
+
self._benchmark_dir = Path.cwd() / "benchmark"
|
|
11
|
+
else:
|
|
12
|
+
self._benchmark_dir = Path(benchmark_dir)
|
|
13
|
+
|
|
14
|
+
async def download_async(self,
|
|
15
|
+
url: str,
|
|
16
|
+
filename: str,
|
|
17
|
+
cached_filename: Optional[str] = None,
|
|
18
|
+
) -> Optional[str]:
|
|
19
|
+
|
|
20
|
+
# Make sure the benchmark directory exists
|
|
21
|
+
self._benchmark_dir.mkdir(parents=True, exist_ok=True)
|
|
22
|
+
|
|
23
|
+
if not cached_filename:
|
|
24
|
+
cached_filename = filename
|
|
25
|
+
|
|
26
|
+
filepath = self._benchmark_dir / filename
|
|
27
|
+
cached_filepath = self._benchmark_dir / cached_filename
|
|
28
|
+
|
|
29
|
+
# If already downloaded, return it
|
|
30
|
+
if cached_filepath.exists():
|
|
31
|
+
if not self._quiet:
|
|
32
|
+
print(f"Using cached: {cached_filename}")
|
|
33
|
+
return str(cached_filepath)
|
|
34
|
+
|
|
35
|
+
if not self._quiet:
|
|
36
|
+
print(f"Downloading: {url}...")
|
|
37
|
+
filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
38
|
+
try:
|
|
39
|
+
async with aiohttp.ClientSession() as session:
|
|
40
|
+
async with session.get(url) as response:
|
|
41
|
+
if not response.ok:
|
|
42
|
+
if not self._quiet:
|
|
43
|
+
print(f"Failed to download {filename}: HTTP {response.status}")
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
content = await response.read()
|
|
47
|
+
with open(filepath, 'wb') as f:
|
|
48
|
+
f.write(content)
|
|
49
|
+
|
|
50
|
+
return str(filepath)
|
|
51
|
+
except Exception as e:
|
|
52
|
+
if not self._quiet:
|
|
53
|
+
print(f"Failed to download {url}: {e}")
|
|
54
|
+
if filepath.exists():
|
|
55
|
+
try:
|
|
56
|
+
filepath.unlink()
|
|
57
|
+
except:
|
|
58
|
+
pass
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
async def download_many_async(self,
|
|
62
|
+
files: List[Tuple[str, str, str, Optional[str]]],
|
|
63
|
+
post_process=None,
|
|
64
|
+
) -> Dict[str, str]:
|
|
65
|
+
tasks = [self.download_async(url, filename, cached_filename) for url, problem_name, filename, cached_filename in files]
|
|
66
|
+
results = await asyncio.gather(*tasks)
|
|
67
|
+
|
|
68
|
+
problem_files = {}
|
|
69
|
+
for (url, problem_name, filename, cached_filename), path in zip(files, results):
|
|
70
|
+
if path:
|
|
71
|
+
if post_process is not None:
|
|
72
|
+
path = post_process(path)
|
|
73
|
+
problem_files[problem_name] = path
|
|
74
|
+
|
|
75
|
+
return problem_files
|
|
76
|
+
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import sys
|
|
5
|
+
import argparse
|
|
6
|
+
from typing import Dict, List, Optional, Tuple
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from gsimplex.benchmarks.downloader import Downloader
|
|
10
|
+
from gsimplex.benchmarks.netlib_emps import expand_mps
|
|
11
|
+
|
|
12
|
+
class NetLibDownloader(Downloader):
|
|
13
|
+
BASE_URL = "https://www.netlib.org/lp/data/"
|
|
14
|
+
|
|
15
|
+
async def download_netlib_benchmarks_async(self, problem_names: List[str]) -> Dict[str, str]:
|
|
16
|
+
files: List[Tuple[str, str, str, Optional[str]]] = [
|
|
17
|
+
(f"{self.BASE_URL}{name}", name, f"netlib/{name}.mps.netlib", f"netlib/{name}.mps")
|
|
18
|
+
for name in problem_names
|
|
19
|
+
if name.strip()
|
|
20
|
+
]
|
|
21
|
+
return await self.download_many_async(files, post_process=NetLibDownloader.post_process_download)
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def post_process_download(netlib_mps_file: str|Path) -> str:
|
|
25
|
+
downloaded_file = Path(netlib_mps_file)
|
|
26
|
+
if not downloaded_file.exists():
|
|
27
|
+
raise FileNotFoundError(f"Downloaded file path mismatch: {downloaded_file} not found!")
|
|
28
|
+
|
|
29
|
+
download_dir = downloaded_file.parent
|
|
30
|
+
target_file = download_dir / downloaded_file.name.removesuffix('.netlib')
|
|
31
|
+
|
|
32
|
+
expand_mps(str(downloaded_file), str(target_file))
|
|
33
|
+
downloaded_file.unlink()
|
|
34
|
+
|
|
35
|
+
assert target_file.exists()
|
|
36
|
+
return str(target_file)
|
|
37
|
+
|
|
38
|
+
async def download_netlib_benchmarks(dir: Optional[str] = None, quiet: bool = False) -> bool:
|
|
39
|
+
downloader = NetLibDownloader(benchmark_dir=dir, quiet=quiet)
|
|
40
|
+
|
|
41
|
+
# All Netlib problems
|
|
42
|
+
problem_names = [
|
|
43
|
+
"25fv47", "80bau3b", "adlittle", "afiro", "agg", "agg2", "agg3",
|
|
44
|
+
"bandm", "beaconfd", "blend", "bnl1", "bnl2", "boeing1", "boeing2",
|
|
45
|
+
"bore3d", "brandy", "capri", "cycle", "czprob", "d2q06c", "d6cube",
|
|
46
|
+
"degen2", "degen3", "dfl001", "e226", "etamacro", "fffff800",
|
|
47
|
+
"finnis", "fit1d", "fit1p", "fit2d", "fit2p", "forplan", "ganges",
|
|
48
|
+
"gfrd-pnc", "greenbea", "greenbeb", "grow7", "grow15", "grow22",
|
|
49
|
+
"israel", "kb2", "lotfi", "maros", "maros-r7", "modszk1", "nesm",
|
|
50
|
+
"perold", "pilot", "pilot.ja", "pilot.we", "pilot4", "pilot87",
|
|
51
|
+
"pilotnov", "recipe", "sc105", "sc205", "sc50a", "sc50b",
|
|
52
|
+
"scagr25", "scagr7", "scfxm1", "scfxm2", "scfxm3", "scorpion",
|
|
53
|
+
"scrs8", "scsd1", "scsd6", "scsd8", "sctap1", "sctap2", "sctap3",
|
|
54
|
+
"seba", "share1b", "share2b", "shell", "ship04l", "ship04s",
|
|
55
|
+
"ship08l", "ship08s", "ship12l", "ship12s", "sierra", "stair",
|
|
56
|
+
"standata", "standgub", "standmps", "stocfor1", "stocfor2",
|
|
57
|
+
"tuff", "vtp.base", "wood1p", "woodw"
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
if not quiet:
|
|
61
|
+
print(f"Downloading {len(problem_names)} Netlib problems...")
|
|
62
|
+
results = await downloader.download_netlib_benchmarks_async(problem_names)
|
|
63
|
+
if not quiet:
|
|
64
|
+
print(f"Downloaded {len(results)} problems successfully")
|
|
65
|
+
|
|
66
|
+
return len(results) == len(problem_names)
|
|
67
|
+
|
|
68
|
+
def main():
|
|
69
|
+
parser = argparse.ArgumentParser(description="Download Netlib benchmarks")
|
|
70
|
+
parser.add_argument('--quiet', action='store_true', help='Run in quiet mode')
|
|
71
|
+
parser.add_argument('--dir', type=str, default=None, help='Directory to save benchmarks')
|
|
72
|
+
args = parser.parse_args()
|
|
73
|
+
|
|
74
|
+
esit = asyncio.run(download_netlib_benchmarks(quiet=args.quiet, dir=args.dir))
|
|
75
|
+
return 0 if esit else 1
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
sys.exit(main())
|