@areb0s/scip.js 1.0.5
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.
- package/README.md +276 -0
- package/dist/basic.html +445 -0
- package/dist/index.mjs +73 -0
- package/dist/scip-core.js +15 -0
- package/dist/scip-worker-client.js +167 -0
- package/dist/scip-worker.js +81 -0
- package/dist/scip-wrapper.js +429 -0
- package/dist/scip.js +3223 -0
- package/dist/scip.js.map +7 -0
- package/dist/scip.min.js +43 -0
- package/dist/scip.min.js.map +7 -0
- package/dist/scip.wasm +0 -0
- package/dist/test-browser.html +118 -0
- package/dist/test-worker.html +161 -0
- package/dist/types.d.ts +230 -0
- package/package.json +66 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>SCIP.js Worker Test (OpenCV-style)</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
10
|
+
max-width: 800px;
|
|
11
|
+
margin: 40px auto;
|
|
12
|
+
padding: 20px;
|
|
13
|
+
background: #1a1a2e;
|
|
14
|
+
color: #e0e0e0;
|
|
15
|
+
}
|
|
16
|
+
h1 { color: #4fc3f7; }
|
|
17
|
+
pre {
|
|
18
|
+
background: #0d1117;
|
|
19
|
+
padding: 15px;
|
|
20
|
+
border-radius: 8px;
|
|
21
|
+
overflow-x: auto;
|
|
22
|
+
white-space: pre-wrap;
|
|
23
|
+
}
|
|
24
|
+
.success { color: #4caf50; }
|
|
25
|
+
.error { color: #f44336; }
|
|
26
|
+
button {
|
|
27
|
+
background: #4fc3f7;
|
|
28
|
+
color: #1a1a2e;
|
|
29
|
+
border: none;
|
|
30
|
+
padding: 10px 20px;
|
|
31
|
+
border-radius: 6px;
|
|
32
|
+
cursor: pointer;
|
|
33
|
+
font-size: 16px;
|
|
34
|
+
margin: 10px 5px 10px 0;
|
|
35
|
+
}
|
|
36
|
+
button:hover { background: #29b6f6; }
|
|
37
|
+
#result { margin-top: 20px; }
|
|
38
|
+
code { background: #0d1117; padding: 2px 6px; border-radius: 4px; }
|
|
39
|
+
</style>
|
|
40
|
+
</head>
|
|
41
|
+
<body>
|
|
42
|
+
<h1>SCIP.js Worker Test (OpenCV-style)</h1>
|
|
43
|
+
<p>Testing CDN loading pattern like OpenCV in Worker</p>
|
|
44
|
+
|
|
45
|
+
<button onclick="testInWorker()">Run Test in Worker</button>
|
|
46
|
+
|
|
47
|
+
<div id="result">
|
|
48
|
+
<pre>Click "Run Test in Worker" to test OpenCV-style loading pattern</pre>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<h2>Worker Code Pattern:</h2>
|
|
52
|
+
<pre>
|
|
53
|
+
// Set base URL before loading
|
|
54
|
+
self.SCIP_BASE_URL = 'https://cdn.../dist/';
|
|
55
|
+
|
|
56
|
+
// Fetch and execute script
|
|
57
|
+
const response = await fetch(self.SCIP_BASE_URL + 'scip.min.js');
|
|
58
|
+
new Function(await response.text())();
|
|
59
|
+
|
|
60
|
+
// Wait for initialization (like OpenCV's cv.ready)
|
|
61
|
+
await self.SCIP.ready;
|
|
62
|
+
|
|
63
|
+
// Use SCIP
|
|
64
|
+
const result = await self.SCIP.solve(`...`);
|
|
65
|
+
</pre>
|
|
66
|
+
|
|
67
|
+
<script>
|
|
68
|
+
function log(msg) {
|
|
69
|
+
document.getElementById('result').innerHTML += msg + '\n';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function testInWorker() {
|
|
73
|
+
document.getElementById('result').innerHTML = '<pre>Starting Worker test...\n';
|
|
74
|
+
|
|
75
|
+
// Get base URL from current page
|
|
76
|
+
const pageBaseUrl = window.location.href.substring(0, window.location.href.lastIndexOf('/') + 1);
|
|
77
|
+
|
|
78
|
+
// Create inline worker
|
|
79
|
+
const workerCode = `
|
|
80
|
+
(async () => {
|
|
81
|
+
try {
|
|
82
|
+
self.postMessage({ type: 'log', msg: 'Worker started' });
|
|
83
|
+
|
|
84
|
+
// Set base URL (passed from main thread)
|
|
85
|
+
self.SCIP_BASE_URL = '${pageBaseUrl}';
|
|
86
|
+
self.postMessage({ type: 'log', msg: 'SCIP_BASE_URL set to: ' + self.SCIP_BASE_URL });
|
|
87
|
+
|
|
88
|
+
// Fetch and execute script (OpenCV pattern)
|
|
89
|
+
self.postMessage({ type: 'log', msg: 'Fetching scip.min.js...' });
|
|
90
|
+
const response = await fetch(self.SCIP_BASE_URL + 'scip.min.js');
|
|
91
|
+
if (!response.ok) throw new Error('Failed to fetch: ' + response.status);
|
|
92
|
+
|
|
93
|
+
const scriptText = await response.text();
|
|
94
|
+
self.postMessage({ type: 'log', msg: 'Script loaded (' + scriptText.length + ' bytes)' });
|
|
95
|
+
|
|
96
|
+
// Execute script
|
|
97
|
+
self.postMessage({ type: 'log', msg: 'Executing script...' });
|
|
98
|
+
new Function(scriptText)();
|
|
99
|
+
|
|
100
|
+
// Check SCIP is available
|
|
101
|
+
if (typeof self.SCIP === 'undefined') {
|
|
102
|
+
throw new Error('SCIP not found in global scope');
|
|
103
|
+
}
|
|
104
|
+
self.postMessage({ type: 'log', msg: 'SCIP object available: ' + Object.keys(self.SCIP).join(', ') });
|
|
105
|
+
|
|
106
|
+
// Wait for ready (like OpenCV's cv.ready)
|
|
107
|
+
self.postMessage({ type: 'log', msg: 'Waiting for SCIP.ready...' });
|
|
108
|
+
await self.SCIP.ready;
|
|
109
|
+
self.postMessage({ type: 'log', msg: 'SCIP initialized!' });
|
|
110
|
+
|
|
111
|
+
// Solve a problem
|
|
112
|
+
self.postMessage({ type: 'log', msg: 'Solving LP problem...' });
|
|
113
|
+
const result = await self.SCIP.solve(\`
|
|
114
|
+
Minimize
|
|
115
|
+
obj: 2 x + 3 y
|
|
116
|
+
Subject To
|
|
117
|
+
c1: x + y >= 4
|
|
118
|
+
c2: 2 x + y >= 5
|
|
119
|
+
Bounds
|
|
120
|
+
x >= 0
|
|
121
|
+
y >= 0
|
|
122
|
+
End
|
|
123
|
+
\`);
|
|
124
|
+
|
|
125
|
+
self.postMessage({ type: 'success', result });
|
|
126
|
+
|
|
127
|
+
} catch (error) {
|
|
128
|
+
self.postMessage({ type: 'error', msg: error.message, stack: error.stack });
|
|
129
|
+
}
|
|
130
|
+
})();
|
|
131
|
+
`;
|
|
132
|
+
|
|
133
|
+
const blob = new Blob([workerCode], { type: 'application/javascript' });
|
|
134
|
+
const worker = new Worker(URL.createObjectURL(blob));
|
|
135
|
+
|
|
136
|
+
worker.onmessage = (e) => {
|
|
137
|
+
const data = e.data;
|
|
138
|
+
if (data.type === 'log') {
|
|
139
|
+
log(data.msg);
|
|
140
|
+
} else if (data.type === 'success') {
|
|
141
|
+
log('<span class="success">SUCCESS!</span>');
|
|
142
|
+
log('Status: ' + data.result.status);
|
|
143
|
+
log('Objective: ' + data.result.objective);
|
|
144
|
+
log('Variables: ' + JSON.stringify(data.result.variables));
|
|
145
|
+
log('</pre>');
|
|
146
|
+
worker.terminate();
|
|
147
|
+
} else if (data.type === 'error') {
|
|
148
|
+
log('<span class="error">ERROR: ' + data.msg + '</span>');
|
|
149
|
+
if (data.stack) log(data.stack);
|
|
150
|
+
log('</pre>');
|
|
151
|
+
worker.terminate();
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
worker.onerror = (e) => {
|
|
156
|
+
log('<span class="error">Worker error: ' + e.message + '</span></pre>');
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
</script>
|
|
160
|
+
</body>
|
|
161
|
+
</html>
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCIP.js TypeScript Type Definitions
|
|
3
|
+
* SCIP Optimization Solver for JavaScript/WebAssembly
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
declare module 'scip.js' {
|
|
7
|
+
/**
|
|
8
|
+
* Solution status
|
|
9
|
+
*/
|
|
10
|
+
export enum Status {
|
|
11
|
+
OPTIMAL = 'optimal',
|
|
12
|
+
INFEASIBLE = 'infeasible',
|
|
13
|
+
UNBOUNDED = 'unbounded',
|
|
14
|
+
TIME_LIMIT = 'timelimit',
|
|
15
|
+
UNKNOWN = 'unknown',
|
|
16
|
+
ERROR = 'error'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Initialization options
|
|
21
|
+
*/
|
|
22
|
+
export interface InitOptions {
|
|
23
|
+
/** Path to scip.wasm file (default: './scip.wasm') */
|
|
24
|
+
wasmPath?: string;
|
|
25
|
+
/** Path to Web Worker script (for worker mode) */
|
|
26
|
+
workerPath?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Solver options
|
|
31
|
+
*/
|
|
32
|
+
export interface SolveOptions {
|
|
33
|
+
/** Input format: 'lp', 'mps', 'zpl' (default: 'lp') */
|
|
34
|
+
format?: 'lp' | 'mps' | 'zpl';
|
|
35
|
+
/** Time limit in seconds (default: 3600) */
|
|
36
|
+
timeLimit?: number;
|
|
37
|
+
/** Relative gap for MIP (e.g., 0.01 for 1%) */
|
|
38
|
+
gap?: number | null;
|
|
39
|
+
/** Enable verbose output */
|
|
40
|
+
verbose?: boolean;
|
|
41
|
+
/** Additional SCIP parameters */
|
|
42
|
+
parameters?: Record<string, string | number | boolean>;
|
|
43
|
+
/** Init options (for worker mode) */
|
|
44
|
+
initOptions?: InitOptions;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Solver statistics
|
|
49
|
+
*/
|
|
50
|
+
export interface Statistics {
|
|
51
|
+
/** Solving time in seconds */
|
|
52
|
+
solvingTime: number | null;
|
|
53
|
+
/** Number of branch-and-bound nodes */
|
|
54
|
+
nodes: number | null;
|
|
55
|
+
/** Number of LP iterations */
|
|
56
|
+
iterations: number | null;
|
|
57
|
+
/** Final optimality gap (percentage) */
|
|
58
|
+
gap: number | null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Solution result
|
|
63
|
+
*/
|
|
64
|
+
export interface Solution {
|
|
65
|
+
/** Solution status */
|
|
66
|
+
status: Status;
|
|
67
|
+
/** Objective function value */
|
|
68
|
+
objective: number | null;
|
|
69
|
+
/** Variable values */
|
|
70
|
+
variables: Record<string, number>;
|
|
71
|
+
/** Solver statistics */
|
|
72
|
+
statistics: Statistics;
|
|
73
|
+
/** Exit code from SCIP */
|
|
74
|
+
exitCode?: number;
|
|
75
|
+
/** Raw output (if verbose mode) */
|
|
76
|
+
output?: string;
|
|
77
|
+
/** Raw solution file content */
|
|
78
|
+
rawSolution?: string | null;
|
|
79
|
+
/** Error message (if status is ERROR) */
|
|
80
|
+
error?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Initialize SCIP WASM module
|
|
85
|
+
* @param options - Initialization options
|
|
86
|
+
*/
|
|
87
|
+
export function init(options?: InitOptions): Promise<void>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Promise that resolves when SCIP is initialized
|
|
91
|
+
* Use like OpenCV's cv.ready
|
|
92
|
+
* @example
|
|
93
|
+
* await SCIP.ready;
|
|
94
|
+
* const result = await SCIP.solve(`...`);
|
|
95
|
+
*/
|
|
96
|
+
export const ready: Promise<void>;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check if SCIP is initialized
|
|
100
|
+
*/
|
|
101
|
+
export function isReady(): boolean;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Solve an optimization problem
|
|
105
|
+
* @param problem - Problem in LP, MPS, or ZIMPL format
|
|
106
|
+
* @param options - Solver options
|
|
107
|
+
* @returns Solution object
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* const result = await solve(`
|
|
112
|
+
* Minimize obj: x + 2 y
|
|
113
|
+
* Subject To
|
|
114
|
+
* c1: x + y >= 1
|
|
115
|
+
* Bounds
|
|
116
|
+
* 0 <= x <= 10
|
|
117
|
+
* 0 <= y <= 10
|
|
118
|
+
* End
|
|
119
|
+
* `);
|
|
120
|
+
*
|
|
121
|
+
* if (result.status === Status.OPTIMAL) {
|
|
122
|
+
* console.log('Optimal value:', result.objective);
|
|
123
|
+
* console.log('x =', result.variables.x);
|
|
124
|
+
* console.log('y =', result.variables.y);
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export function solve(problem: string, options?: SolveOptions): Promise<Solution>;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Solve a minimization problem
|
|
132
|
+
* Convenience wrapper that ensures minimization
|
|
133
|
+
*/
|
|
134
|
+
export function minimize(problem: string, options?: SolveOptions): Promise<Solution>;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Solve a maximization problem
|
|
138
|
+
* Convenience wrapper that ensures maximization
|
|
139
|
+
*/
|
|
140
|
+
export function maximize(problem: string, options?: SolveOptions): Promise<Solution>;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get SCIP version info
|
|
144
|
+
*/
|
|
145
|
+
export function version(): Promise<string>;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get available SCIP parameters
|
|
149
|
+
*/
|
|
150
|
+
export function getParameters(): Promise<string>;
|
|
151
|
+
|
|
152
|
+
// Worker API
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Initialize SCIP in a Web Worker
|
|
156
|
+
*/
|
|
157
|
+
export function initWorker(options?: InitOptions): Promise<void>;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Solve in a Web Worker (non-blocking)
|
|
161
|
+
*/
|
|
162
|
+
export function solveInWorker(problem: string, options?: SolveOptions): Promise<Solution>;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Minimize in a Web Worker
|
|
166
|
+
*/
|
|
167
|
+
export function minimizeInWorker(problem: string, options?: SolveOptions): Promise<Solution>;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Maximize in a Web Worker
|
|
171
|
+
*/
|
|
172
|
+
export function maximizeInWorker(problem: string, options?: SolveOptions): Promise<Solution>;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Terminate the Web Worker
|
|
176
|
+
*/
|
|
177
|
+
export function terminateWorker(): void;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Worker solver instance
|
|
181
|
+
*/
|
|
182
|
+
export interface WorkerSolver {
|
|
183
|
+
solve(problem: string, options?: SolveOptions): Promise<Solution>;
|
|
184
|
+
minimize(problem: string, options?: SolveOptions): Promise<Solution>;
|
|
185
|
+
maximize(problem: string, options?: SolveOptions): Promise<Solution>;
|
|
186
|
+
version(): Promise<string>;
|
|
187
|
+
isReady(): Promise<boolean>;
|
|
188
|
+
terminate(): void;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Create a worker-based solver instance
|
|
193
|
+
* Use for long-running optimizations to avoid blocking the main thread
|
|
194
|
+
*/
|
|
195
|
+
export function createWorkerSolver(options?: InitOptions): Promise<WorkerSolver>;
|
|
196
|
+
|
|
197
|
+
// Default export
|
|
198
|
+
const SCIP: {
|
|
199
|
+
init: typeof init;
|
|
200
|
+
ready: typeof ready;
|
|
201
|
+
isReady: typeof isReady;
|
|
202
|
+
solve: typeof solve;
|
|
203
|
+
minimize: typeof minimize;
|
|
204
|
+
maximize: typeof maximize;
|
|
205
|
+
version: typeof version;
|
|
206
|
+
getParameters: typeof getParameters;
|
|
207
|
+
Status: typeof Status;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
export default SCIP;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// LP Format Problem Helper Types
|
|
214
|
+
declare module 'scip.js/lp' {
|
|
215
|
+
/**
|
|
216
|
+
* LP Problem builder (optional convenience API)
|
|
217
|
+
*/
|
|
218
|
+
export interface LPProblem {
|
|
219
|
+
minimize(expression: string): LPProblem;
|
|
220
|
+
maximize(expression: string): LPProblem;
|
|
221
|
+
subjectTo(name: string, constraint: string): LPProblem;
|
|
222
|
+
bounds(variable: string, lower?: number, upper?: number): LPProblem;
|
|
223
|
+
binary(...variables: string[]): LPProblem;
|
|
224
|
+
integer(...variables: string[]): LPProblem;
|
|
225
|
+
general(...variables: string[]): LPProblem;
|
|
226
|
+
toString(): string;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function createProblem(): LPProblem;
|
|
230
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@areb0s/scip.js",
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "SCIP Optimization Solver compiled to WebAssembly - LP, MIP, and MINLP support",
|
|
5
|
+
"main": "dist/index.mjs",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/types.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/types.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./worker": {
|
|
16
|
+
"types": "./dist/types.d.ts",
|
|
17
|
+
"import": "./dist/scip-worker-client.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist/",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"unpkg": "dist/scip.browser.min.js",
|
|
26
|
+
"jsdelivr": "dist/scip.browser.min.js",
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "bash build.sh",
|
|
29
|
+
"build:docker": "docker build -t scip-wasm-builder .",
|
|
30
|
+
"build:browser": "node scripts/build-browser.mjs",
|
|
31
|
+
"test": "node examples/test.mjs",
|
|
32
|
+
"serve": "npx http-server dist -p 8080 --cors",
|
|
33
|
+
"clean": "rm -rf dist/ build/"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/areb0s/scip.js"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"scip",
|
|
41
|
+
"optimization",
|
|
42
|
+
"solver",
|
|
43
|
+
"linear-programming",
|
|
44
|
+
"mixed-integer-programming",
|
|
45
|
+
"minlp",
|
|
46
|
+
"wasm",
|
|
47
|
+
"webassembly",
|
|
48
|
+
"operations-research",
|
|
49
|
+
"constraint-programming"
|
|
50
|
+
],
|
|
51
|
+
"author": "",
|
|
52
|
+
"license": "Apache-2.0",
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/areb0s/scip.js"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://github.com/areb0s/scip.js",
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"esbuild": "^0.24.0"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=18.0.0"
|
|
62
|
+
},
|
|
63
|
+
"browser": {
|
|
64
|
+
"./dist/index.mjs": "./dist/index.mjs"
|
|
65
|
+
}
|
|
66
|
+
}
|