chore: 初始化 MCM/ICM Toolkit 仓库骨架
审查并精简 ChatGPT 的建库建议后落地的第一版骨架,只定接口不写实现。 - 建议审查:14 个包 → 7 个;约 250 个函数 → 约 40 个;5 个 Result 类 → 1 个 (docs/01-suggestion-review.md) - docs/:架构、路线图(含永久不做清单)、环境、Git 工作流、函数卡片模板 - src/mcm_toolkit/:core / data / stats / models / solve / decision / viz 共 23 个模块,均为签名 + docstring + NotImplementedError - tests/:9 个文件,其中 test_api_surface.py 已通过(20 passed) - examples/ problems/ notebooks/ figures/ tables/ 骨架 - pyproject.toml(核心依赖 4 个 + 6 组 extras)、environment.yml - scripts/:setup-venv.ps1(ASCII + LF)、setup-conda.ps1、doctor.ps1 - 环境:.venv(system-site-packages)与 conda 环境 mcm-toolkit 均已建好, 数值栈版本与 base 完全对齐(numpy 2.4.6 / scipy 1.18.1 / pandas 3.0.6,MKL 后端)
This commit is contained in:
commit
5319589dfd
76 files changed
+6812
No files matched your search
@@ -0,0 +1,103 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Environment self-check: interpreters, conda envs, dependencies, package import, tests.
|
||||
|
||||
.DESCRIPTION
|
||||
Run this on day one of a contest so environment problems surface before you
|
||||
start working, instead of three hours before the deadline.
|
||||
|
||||
Checks both environments:
|
||||
* .venv lightweight, offline-safe (10 MB)
|
||||
* conda env "mcm-toolkit" the main working environment
|
||||
|
||||
NOTE: this file is intentionally pure ASCII, UTF-8 without BOM, LF endings.
|
||||
Windows PowerShell 5.1 parses .ps1 as ANSI when there is no BOM, so non-ASCII
|
||||
characters (e.g. Chinese) turn into illegal bytes and cause syntax errors.
|
||||
|
||||
.PARAMETER SkipTests
|
||||
Do not run pytest.
|
||||
|
||||
.EXAMPLE
|
||||
.\scripts\doctor.ps1
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[switch]$SkipTests
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
|
||||
# Python snippet kept as a single-quoted here-string: it must not contain double
|
||||
# quotes, because PowerShell 5.1 strips them when passing arguments to native programs.
|
||||
$probe = @'
|
||||
import importlib
|
||||
mods = ['numpy','scipy','pandas','matplotlib','sklearn','statsmodels',
|
||||
'networkx','sympy','pytest','ruff','SALib','pymoo','pyright','openpyxl']
|
||||
for name in mods:
|
||||
try:
|
||||
mod = importlib.import_module(name)
|
||||
ver = getattr(mod, '__version__', '')
|
||||
print(' OK {:12s} {}'.format(name, ver))
|
||||
except Exception as exc:
|
||||
print(' MISS {:12s} ({})'.format(name, type(exc).__name__))
|
||||
'@
|
||||
|
||||
function Report-Interpreter {
|
||||
param([string]$Label, [string]$Py)
|
||||
|
||||
Write-Host ''
|
||||
Write-Host "--- $Label ---" -ForegroundColor Cyan
|
||||
if (-not (Test-Path -LiteralPath $Py)) {
|
||||
Write-Host " not found: $Py" -ForegroundColor Yellow
|
||||
return
|
||||
}
|
||||
& $Py -c "import sys; print(' exe :', sys.executable); print(' python:', sys.version.split()[0])"
|
||||
& $Py -c $probe
|
||||
& $Py -c "import mcm_toolkit as m; print(' mcm_toolkit', m.__version__, '/', len(m.__all__), 'public names')"
|
||||
}
|
||||
|
||||
Write-Host '=== MCM/ICM Toolkit environment check ===' -ForegroundColor Cyan
|
||||
Write-Host "repo: $repoRoot"
|
||||
|
||||
Report-Interpreter -Label 'venv (.venv) - lightweight / offline-safe' -Py (Join-Path $repoRoot '.venv\Scripts\python.exe')
|
||||
|
||||
$conda = Get-Command conda -ErrorAction SilentlyContinue
|
||||
$condaExe = if ($conda) { $conda.Source } else { $null }
|
||||
if (-not $condaExe) {
|
||||
foreach ($c in @('D:\anaconda\Scripts\conda.exe', "$env:USERPROFILE\anaconda3\Scripts\conda.exe")) {
|
||||
if (Test-Path -LiteralPath $c) { $condaExe = $c; break }
|
||||
}
|
||||
}
|
||||
|
||||
if ($condaExe) {
|
||||
Write-Host ''
|
||||
Write-Host "--- conda ($condaExe) ---" -ForegroundColor Cyan
|
||||
& $condaExe --version | ForEach-Object { Write-Host " $_" }
|
||||
Write-Host ' environments:'
|
||||
& $condaExe env list | ForEach-Object { Write-Host " $_" }
|
||||
|
||||
$condaPy = Join-Path (Split-Path -Parent (Split-Path -Parent $condaExe)) 'envs\mcm-toolkit\python.exe'
|
||||
Report-Interpreter -Label 'conda env "mcm-toolkit" - main working env' -Py $condaPy
|
||||
} else {
|
||||
Write-Host ''
|
||||
Write-Host '--- conda ---' -ForegroundColor Cyan
|
||||
Write-Host ' conda not found on PATH or in the usual locations' -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
if (-not $SkipTests) {
|
||||
Write-Host ''
|
||||
Write-Host '--- tests ---' -ForegroundColor Cyan
|
||||
Push-Location $repoRoot
|
||||
foreach ($py in @((Join-Path $repoRoot '.venv\Scripts\python.exe'), $condaPy)) {
|
||||
if ($py -and (Test-Path -LiteralPath $py)) {
|
||||
Write-Host " $py"
|
||||
& $py -m pytest -p no:cacheprovider -q 2>&1 | Select-Object -Last 1 | ForEach-Object { Write-Host " $_" }
|
||||
}
|
||||
}
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
Write-Host ''
|
||||
Write-Host 'Check finished.' -ForegroundColor Cyan
|
||||
@@ -0,0 +1,136 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Create the conda environment "mcm-toolkit".
|
||||
|
||||
.DESCRIPTION
|
||||
Three ways to build the environment, in order of preference:
|
||||
|
||||
(default) explicit package list, installed from the "defaults" channel.
|
||||
Needs internet. Measured on this machine: ~14.5 min, ~3 GB download.
|
||||
-FromYml same, but driven by environment.yml (better for reproducing later).
|
||||
-CloneBase mirrors the whole base environment. NOTE: on conda 25.11 this is NOT
|
||||
a fast local copy -- conda replays base's full package list and
|
||||
downloads/installs it. Use it only when you want an exact copy of base.
|
||||
|
||||
To work offline later, build the environment BEFORE the contest and keep the
|
||||
conda package cache (D:\anaconda\pkgs) intact.
|
||||
|
||||
NOTE: this file is intentionally pure ASCII, UTF-8 without BOM, LF endings.
|
||||
Windows PowerShell 5.1 parses .ps1 as ANSI when there is no BOM, so non-ASCII
|
||||
characters (e.g. Chinese) turn into illegal bytes and cause syntax errors.
|
||||
|
||||
.PARAMETER Name
|
||||
conda environment name. Default: mcm-toolkit.
|
||||
|
||||
.PARAMETER FromYml
|
||||
Build from environment.yml instead of the inline package list.
|
||||
|
||||
.PARAMETER CloneBase
|
||||
Clone the base environment instead of installing a explicit package list.
|
||||
|
||||
.PARAMETER Force
|
||||
Remove and recreate the environment if it already exists.
|
||||
|
||||
.EXAMPLE
|
||||
.\scripts\setup-conda.ps1
|
||||
Create "mcm-toolkit" with the science stack (needs internet).
|
||||
|
||||
.EXAMPLE
|
||||
.\scripts\setup-conda.ps1 -FromYml -Force
|
||||
Rebuild from environment.yml.
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$Name = 'mcm-toolkit',
|
||||
[switch]$FromYml,
|
||||
[switch]$CloneBase,
|
||||
[switch]$Force
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
|
||||
# Packages required by pyproject.toml's core deps plus the [ml] / [graph] /
|
||||
# [symbolic] / [excel] / [dev] extras. SALib, pymoo and pyright are pip-only
|
||||
# (see install after the conda step).
|
||||
$packages = @(
|
||||
'python=3.13',
|
||||
'numpy', 'scipy', 'pandas', 'matplotlib',
|
||||
'scikit-learn', 'statsmodels',
|
||||
'networkx', 'sympy', 'openpyxl',
|
||||
'pytest', 'ruff',
|
||||
'pip'
|
||||
)
|
||||
$pipOnly = @('SALib', 'pymoo', 'pyright')
|
||||
|
||||
function Find-Conda {
|
||||
$candidates = @(
|
||||
"$env:USERPROFILE\anaconda3\Scripts\conda.exe",
|
||||
"$env:USERPROFILE\miniconda3\Scripts\conda.exe",
|
||||
'D:\anaconda\Scripts\conda.exe',
|
||||
'C:\ProgramData\Anaconda3\Scripts\conda.exe',
|
||||
'C:\ProgramData\miniconda3\Scripts\conda.exe'
|
||||
)
|
||||
foreach ($c in $candidates) { if (Test-Path -LiteralPath $c) { return $c } }
|
||||
$onPath = Get-Command conda -ErrorAction SilentlyContinue
|
||||
if ($onPath) { return $onPath.Source }
|
||||
throw 'conda not found. Install Anaconda/Miniconda first.'
|
||||
}
|
||||
|
||||
$conda = Find-Conda
|
||||
Write-Host "[1/5] conda = $conda"
|
||||
|
||||
# PITFALL: the Anaconda 25.x ToS plugin fetches channel metadata from
|
||||
# repo.anaconda.com before every create/install and crashes when offline.
|
||||
# This env var auto-accepts the ToS: it skips the network call while keeping
|
||||
# the libmamba solver (unlike --no-plugins, which disables all plugins).
|
||||
$env:CONDA_PLUGINS_AUTO_ACCEPT_TOS = 'yes'
|
||||
$env:CONDA_AUTO_UPDATE_CONDA = 'false'
|
||||
|
||||
# PITFALL: envs_dirs and pkgs_dirs must be writable, otherwise conda fails with
|
||||
# NotWritableError or "Error encountered while attempting to create cache directory".
|
||||
Write-Host '[2/5] current envs_dirs / pkgs_dirs:'
|
||||
& $conda config --show envs_dirs pkgs_dirs 2>$null | ForEach-Object { Write-Host " $_" }
|
||||
|
||||
$existing = (& $conda env list --json | ConvertFrom-Json).envs |
|
||||
Where-Object { (Split-Path -Leaf $_) -eq $Name }
|
||||
if ($existing) {
|
||||
if (-not $Force) {
|
||||
Write-Host "Environment '$Name' already exists ($existing). Use -Force to rebuild." -ForegroundColor Yellow
|
||||
exit 0
|
||||
}
|
||||
Write-Host "Environment '$Name' exists, -Force given, removing first ..."
|
||||
& $conda env remove -n $Name -y
|
||||
}
|
||||
|
||||
Write-Host '[3/5] creating the environment (this can take 10-20 minutes) ...'
|
||||
$sw = [Diagnostics.Stopwatch]::StartNew()
|
||||
if ($FromYml) {
|
||||
$yml = Join-Path $repoRoot 'environment.yml'
|
||||
if (-not (Test-Path -LiteralPath $yml)) { throw "Not found: $yml" }
|
||||
& $conda env create -n $Name -f $yml
|
||||
} elseif ($CloneBase) {
|
||||
& $conda create -n $Name --clone base -y
|
||||
} else {
|
||||
& $conda create -n $Name -y @packages
|
||||
}
|
||||
if ($LASTEXITCODE -ne 0) { throw "conda create failed (exit code $LASTEXITCODE)" }
|
||||
Write-Host (" done in {0:N0}s" -f $sw.Elapsed.TotalSeconds)
|
||||
|
||||
$envPy = Join-Path (Split-Path -Parent (Split-Path -Parent $conda)) "envs\$Name\python.exe"
|
||||
if (-not (Test-Path -LiteralPath $envPy)) {
|
||||
$envsRoot = (& $conda config --show envs_dirs | Select-String -Pattern '\S' | Select-Object -First 1).ToString().Trim()
|
||||
$envPy = Join-Path $envsRoot "$Name\python.exe"
|
||||
}
|
||||
|
||||
Write-Host '[4/5] installing pip-only extras (SALib, pymoo, pyright) + this project ...'
|
||||
& $envPy -m pip install --disable-pip-version-check --no-warn-script-location -q @pipOnly
|
||||
& $envPy -m pip install -e $repoRoot --no-deps --no-build-isolation --disable-pip-version-check -q
|
||||
|
||||
Write-Host '[5/5] verifying ...'
|
||||
& $envPy -c "import numpy, scipy, pandas, matplotlib, sklearn, statsmodels, networkx, sympy, SALib, pymoo, mcm_toolkit; print(' numpy', numpy.__version__, '| pandas', pandas.__version__, '| sklearn', sklearn.__version__); print(' mcm_toolkit', mcm_toolkit.__version__)"
|
||||
|
||||
Write-Host ''
|
||||
Write-Host 'Done. Activate with:' -ForegroundColor Green
|
||||
Write-Host " conda activate $Name"
|
||||
@@ -0,0 +1,71 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Create .venv inside the repo and install mcm-toolkit in editable mode.
|
||||
|
||||
.DESCRIPTION
|
||||
Uses --system-site-packages so the venv reuses the scientific stack already
|
||||
installed in Anaconda base. That makes the whole step OFFLINE: no package is
|
||||
downloaded, yet numpy / scipy / pandas / matplotlib / sklearn / statsmodels /
|
||||
networkx / sympy are all importable.
|
||||
|
||||
The editable install uses --no-deps --no-build-isolation: dependencies are
|
||||
already provided by base, and pip must not go online for a build backend.
|
||||
|
||||
NOTE: this file is intentionally pure ASCII and UTF-8 without BOM.
|
||||
Windows PowerShell 5.1 parses .ps1 as ANSI when there is no BOM, so non-ASCII
|
||||
characters (e.g. Chinese) turn into illegal bytes and cause syntax errors.
|
||||
|
||||
.PARAMETER Python
|
||||
Interpreter used to create the venv. Default: D:\anaconda\python.exe
|
||||
|
||||
.PARAMETER Recreate
|
||||
Delete and recreate .venv if it already exists.
|
||||
|
||||
.EXAMPLE
|
||||
.\scripts\setup-venv.ps1
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$Python = 'D:\anaconda\python.exe',
|
||||
[switch]$Recreate
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
$venvDir = Join-Path $repoRoot '.venv'
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Python)) {
|
||||
$cmd = Get-Command python -ErrorAction SilentlyContinue
|
||||
if (-not $cmd) { throw "Python interpreter not found: $Python" }
|
||||
$Python = $cmd.Source
|
||||
Write-Host "Using python from PATH: $Python" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
if (Test-Path -LiteralPath $venvDir) {
|
||||
if (-not $Recreate) {
|
||||
Write-Host '.venv already exists. Use -Recreate to rebuild.' -ForegroundColor Yellow
|
||||
} else {
|
||||
Write-Host 'Removing existing .venv ...'
|
||||
Remove-Item -LiteralPath $venvDir -Recurse -Force
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $venvDir)) {
|
||||
Write-Host '[1/3] creating .venv (--system-site-packages, reuses Anaconda base) ...'
|
||||
& $Python -m venv --system-site-packages --prompt 'mcm-toolkit' $venvDir
|
||||
if ($LASTEXITCODE -ne 0) { throw 'venv creation failed' }
|
||||
}
|
||||
|
||||
$venvPy = Join-Path $venvDir 'Scripts\python.exe'
|
||||
|
||||
Write-Host '[2/3] editable install of mcm-toolkit (--no-deps --no-build-isolation) ...'
|
||||
& $venvPy -m pip install -e $repoRoot --no-deps --no-build-isolation --disable-pip-version-check
|
||||
if ($LASTEXITCODE -ne 0) { throw 'pip install -e failed' }
|
||||
|
||||
Write-Host '[3/3] verifying ...'
|
||||
& $venvPy -c "import mcm_toolkit, numpy, scipy, pandas, matplotlib; print('mcm_toolkit', mcm_toolkit.__version__); print('numpy', numpy.__version__)"
|
||||
|
||||
Write-Host ''
|
||||
Write-Host 'Done. Activate with:' -ForegroundColor Green
|
||||
Write-Host ' .\.venv\Scripts\Activate.ps1'
|
||||
Reference in new issue
Block a user