@areb0s/scip.js 1.1.7 → 1.1.8
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/dist/scip-wrapper.js +18 -15
- package/package.json +1 -1
- package/dist/basic.html +0 -445
- package/dist/test-browser.html +0 -118
- package/dist/test-worker.html +0 -161
package/dist/scip-wrapper.js
CHANGED
|
@@ -88,29 +88,32 @@ function parseStatus(output) {
|
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
90
|
* Parse solution values from SCIP output
|
|
91
|
+
* @param {string} output - stdout from SCIP
|
|
92
|
+
* @param {string} rawSolution - solution file content (more reliable)
|
|
91
93
|
*/
|
|
92
|
-
function parseSolution(output) {
|
|
94
|
+
function parseSolution(output, rawSolution = null) {
|
|
93
95
|
const variables = {};
|
|
94
96
|
const objective = { value: null, sense: null };
|
|
95
97
|
|
|
98
|
+
// Use rawSolution if available (more reliable)
|
|
99
|
+
const solText = rawSolution || output;
|
|
100
|
+
|
|
96
101
|
// Parse objective value
|
|
97
|
-
const objMatch =
|
|
102
|
+
const objMatch = solText.match(/objective value:\s*([\d.e+-]+)/i);
|
|
98
103
|
if (objMatch) {
|
|
99
104
|
objective.value = parseFloat(objMatch[1]);
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
// Parse variable values from solution display
|
|
103
|
-
//
|
|
104
|
-
|
|
108
|
+
// Match ZIMPL-style variable names: x$sun#0, effSum$star#1, b_sun_10, etc.
|
|
109
|
+
// Format: variableName value (obj:coef)
|
|
110
|
+
const varRegex = /^([\w$#]+)\s+([\d.e+-]+)/gm;
|
|
105
111
|
let match;
|
|
106
112
|
|
|
107
|
-
|
|
108
|
-
const solSection = output.split('solution:')[1] || output;
|
|
109
|
-
|
|
110
|
-
while ((match = varRegex.exec(solSection)) !== null) {
|
|
113
|
+
while ((match = varRegex.exec(solText)) !== null) {
|
|
111
114
|
const name = match[1];
|
|
112
115
|
const value = parseFloat(match[2]);
|
|
113
|
-
if (!isNaN(value) && name !== 'objective') {
|
|
116
|
+
if (!isNaN(value) && name !== 'objective' && name !== 'solution') {
|
|
114
117
|
variables[name] = value;
|
|
115
118
|
}
|
|
116
119
|
}
|
|
@@ -329,12 +332,7 @@ export async function solve(problem, options = {}) {
|
|
|
329
332
|
// Run SCIP with batch mode
|
|
330
333
|
const exitCode = scipModule.callMain(['-b', '/settings/commands.txt']);
|
|
331
334
|
|
|
332
|
-
//
|
|
333
|
-
const status = parseStatus(stdout);
|
|
334
|
-
const { variables, objective } = parseSolution(stdout);
|
|
335
|
-
const statistics = parseStatistics(stdout);
|
|
336
|
-
|
|
337
|
-
// Try to read solution file
|
|
335
|
+
// Try to read solution file first (more reliable for parsing)
|
|
338
336
|
let rawSolution = null;
|
|
339
337
|
try {
|
|
340
338
|
rawSolution = scipModule.FS.readFile(solutionFile, { encoding: 'utf8' });
|
|
@@ -342,6 +340,11 @@ export async function solve(problem, options = {}) {
|
|
|
342
340
|
// Solution file may not exist if infeasible
|
|
343
341
|
}
|
|
344
342
|
|
|
343
|
+
// Parse results
|
|
344
|
+
const status = parseStatus(stdout);
|
|
345
|
+
const { variables, objective } = parseSolution(stdout, rawSolution);
|
|
346
|
+
const statistics = parseStatistics(stdout);
|
|
347
|
+
|
|
345
348
|
return {
|
|
346
349
|
status,
|
|
347
350
|
objective: objective.value,
|
package/package.json
CHANGED
package/dist/basic.html
DELETED
|
@@ -1,445 +0,0 @@
|
|
|
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 - Optimization Solver Demo</title>
|
|
7
|
-
<style>
|
|
8
|
-
* {
|
|
9
|
-
box-sizing: border-box;
|
|
10
|
-
margin: 0;
|
|
11
|
-
padding: 0;
|
|
12
|
-
}
|
|
13
|
-
body {
|
|
14
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
15
|
-
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
16
|
-
min-height: 100vh;
|
|
17
|
-
color: #e0e0e0;
|
|
18
|
-
padding: 2rem;
|
|
19
|
-
}
|
|
20
|
-
.container {
|
|
21
|
-
max-width: 1000px;
|
|
22
|
-
margin: 0 auto;
|
|
23
|
-
}
|
|
24
|
-
h1 {
|
|
25
|
-
text-align: center;
|
|
26
|
-
margin-bottom: 0.5rem;
|
|
27
|
-
color: #4fc3f7;
|
|
28
|
-
}
|
|
29
|
-
.subtitle {
|
|
30
|
-
text-align: center;
|
|
31
|
-
color: #888;
|
|
32
|
-
margin-bottom: 2rem;
|
|
33
|
-
}
|
|
34
|
-
.card {
|
|
35
|
-
background: rgba(255, 255, 255, 0.05);
|
|
36
|
-
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
37
|
-
border-radius: 12px;
|
|
38
|
-
padding: 1.5rem;
|
|
39
|
-
margin-bottom: 1.5rem;
|
|
40
|
-
}
|
|
41
|
-
.card h2 {
|
|
42
|
-
color: #4fc3f7;
|
|
43
|
-
margin-bottom: 1rem;
|
|
44
|
-
font-size: 1.2rem;
|
|
45
|
-
}
|
|
46
|
-
.examples {
|
|
47
|
-
display: flex;
|
|
48
|
-
gap: 0.5rem;
|
|
49
|
-
flex-wrap: wrap;
|
|
50
|
-
margin-bottom: 1rem;
|
|
51
|
-
}
|
|
52
|
-
.example-btn {
|
|
53
|
-
padding: 0.5rem 1rem;
|
|
54
|
-
background: rgba(79, 195, 247, 0.2);
|
|
55
|
-
border: 1px solid rgba(79, 195, 247, 0.3);
|
|
56
|
-
border-radius: 6px;
|
|
57
|
-
color: #4fc3f7;
|
|
58
|
-
cursor: pointer;
|
|
59
|
-
font-size: 0.9rem;
|
|
60
|
-
transition: all 0.2s;
|
|
61
|
-
}
|
|
62
|
-
.example-btn:hover {
|
|
63
|
-
background: rgba(79, 195, 247, 0.3);
|
|
64
|
-
}
|
|
65
|
-
textarea {
|
|
66
|
-
width: 100%;
|
|
67
|
-
height: 300px;
|
|
68
|
-
background: #0d1117;
|
|
69
|
-
border: 1px solid #30363d;
|
|
70
|
-
border-radius: 8px;
|
|
71
|
-
color: #c9d1d9;
|
|
72
|
-
font-family: 'Monaco', 'Menlo', monospace;
|
|
73
|
-
font-size: 13px;
|
|
74
|
-
padding: 1rem;
|
|
75
|
-
resize: vertical;
|
|
76
|
-
}
|
|
77
|
-
.options {
|
|
78
|
-
display: grid;
|
|
79
|
-
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
80
|
-
gap: 1rem;
|
|
81
|
-
margin-bottom: 1rem;
|
|
82
|
-
}
|
|
83
|
-
.option {
|
|
84
|
-
display: flex;
|
|
85
|
-
flex-direction: column;
|
|
86
|
-
gap: 0.3rem;
|
|
87
|
-
}
|
|
88
|
-
.option label {
|
|
89
|
-
font-size: 0.85rem;
|
|
90
|
-
color: #888;
|
|
91
|
-
}
|
|
92
|
-
.option input, .option select {
|
|
93
|
-
padding: 0.5rem;
|
|
94
|
-
background: #0d1117;
|
|
95
|
-
border: 1px solid #30363d;
|
|
96
|
-
border-radius: 4px;
|
|
97
|
-
color: #c9d1d9;
|
|
98
|
-
}
|
|
99
|
-
.solve-btn {
|
|
100
|
-
width: 100%;
|
|
101
|
-
padding: 1rem;
|
|
102
|
-
background: linear-gradient(135deg, #4fc3f7, #29b6f6);
|
|
103
|
-
border: none;
|
|
104
|
-
border-radius: 8px;
|
|
105
|
-
color: #1a1a2e;
|
|
106
|
-
font-size: 1.1rem;
|
|
107
|
-
font-weight: 600;
|
|
108
|
-
cursor: pointer;
|
|
109
|
-
transition: transform 0.2s;
|
|
110
|
-
}
|
|
111
|
-
.solve-btn:hover:not(:disabled) {
|
|
112
|
-
transform: scale(1.02);
|
|
113
|
-
}
|
|
114
|
-
.solve-btn:disabled {
|
|
115
|
-
opacity: 0.6;
|
|
116
|
-
cursor: not-allowed;
|
|
117
|
-
}
|
|
118
|
-
.result {
|
|
119
|
-
background: #0d1117;
|
|
120
|
-
border-radius: 8px;
|
|
121
|
-
padding: 1rem;
|
|
122
|
-
}
|
|
123
|
-
.status {
|
|
124
|
-
display: inline-block;
|
|
125
|
-
padding: 0.3rem 0.8rem;
|
|
126
|
-
border-radius: 4px;
|
|
127
|
-
font-weight: 600;
|
|
128
|
-
margin-bottom: 1rem;
|
|
129
|
-
}
|
|
130
|
-
.status.optimal { background: #238636; color: white; }
|
|
131
|
-
.status.infeasible { background: #da3633; color: white; }
|
|
132
|
-
.status.unknown { background: #9e6a03; color: white; }
|
|
133
|
-
.status.error { background: #da3633; color: white; }
|
|
134
|
-
.result-grid {
|
|
135
|
-
display: grid;
|
|
136
|
-
grid-template-columns: 1fr 1fr;
|
|
137
|
-
gap: 1rem;
|
|
138
|
-
}
|
|
139
|
-
.result-section h3 {
|
|
140
|
-
color: #4fc3f7;
|
|
141
|
-
margin-bottom: 0.5rem;
|
|
142
|
-
font-size: 1rem;
|
|
143
|
-
}
|
|
144
|
-
.result-section pre {
|
|
145
|
-
background: rgba(0,0,0,0.3);
|
|
146
|
-
padding: 0.8rem;
|
|
147
|
-
border-radius: 4px;
|
|
148
|
-
font-size: 0.9rem;
|
|
149
|
-
overflow-x: auto;
|
|
150
|
-
}
|
|
151
|
-
.loading {
|
|
152
|
-
display: none;
|
|
153
|
-
text-align: center;
|
|
154
|
-
padding: 2rem;
|
|
155
|
-
}
|
|
156
|
-
.loading.active {
|
|
157
|
-
display: block;
|
|
158
|
-
}
|
|
159
|
-
.spinner {
|
|
160
|
-
width: 40px;
|
|
161
|
-
height: 40px;
|
|
162
|
-
border: 3px solid rgba(79, 195, 247, 0.3);
|
|
163
|
-
border-top-color: #4fc3f7;
|
|
164
|
-
border-radius: 50%;
|
|
165
|
-
animation: spin 1s linear infinite;
|
|
166
|
-
margin: 0 auto 1rem;
|
|
167
|
-
}
|
|
168
|
-
@keyframes spin {
|
|
169
|
-
to { transform: rotate(360deg); }
|
|
170
|
-
}
|
|
171
|
-
.footer {
|
|
172
|
-
text-align: center;
|
|
173
|
-
margin-top: 2rem;
|
|
174
|
-
color: #666;
|
|
175
|
-
font-size: 0.85rem;
|
|
176
|
-
}
|
|
177
|
-
.footer a {
|
|
178
|
-
color: #4fc3f7;
|
|
179
|
-
text-decoration: none;
|
|
180
|
-
}
|
|
181
|
-
</style>
|
|
182
|
-
</head>
|
|
183
|
-
<body>
|
|
184
|
-
<div class="container">
|
|
185
|
-
<h1>SCIP.js</h1>
|
|
186
|
-
<p class="subtitle">SCIP Optimization Solver in WebAssembly - LP, MIP, MINLP Support</p>
|
|
187
|
-
|
|
188
|
-
<div class="card">
|
|
189
|
-
<h2>Problem Input (LP Format)</h2>
|
|
190
|
-
<div class="examples">
|
|
191
|
-
<button class="example-btn" onclick="loadExample('lp')">LP</button>
|
|
192
|
-
<button class="example-btn" onclick="loadExample('mip')">MIP</button>
|
|
193
|
-
<button class="example-btn" onclick="loadExample('minlp')">MINLP (Quadratic)</button>
|
|
194
|
-
<button class="example-btn" onclick="loadExample('minlp2')">MINLP (Facility)</button>
|
|
195
|
-
<button class="example-btn" onclick="loadExample('knapsack')">Knapsack</button>
|
|
196
|
-
<button class="example-btn" onclick="loadExample('transportation')">Transportation</button>
|
|
197
|
-
</div>
|
|
198
|
-
<textarea id="problem"></textarea>
|
|
199
|
-
</div>
|
|
200
|
-
|
|
201
|
-
<div class="card">
|
|
202
|
-
<h2>Solver Options</h2>
|
|
203
|
-
<div class="options">
|
|
204
|
-
<div class="option">
|
|
205
|
-
<label>Time Limit (seconds)</label>
|
|
206
|
-
<input type="number" id="timeLimit" value="60" min="1" max="3600">
|
|
207
|
-
</div>
|
|
208
|
-
<div class="option">
|
|
209
|
-
<label>Gap Tolerance (%)</label>
|
|
210
|
-
<input type="number" id="gap" value="0" min="0" max="100" step="0.1">
|
|
211
|
-
</div>
|
|
212
|
-
<div class="option">
|
|
213
|
-
<label>Verbose Output</label>
|
|
214
|
-
<select id="verbose">
|
|
215
|
-
<option value="false">No</option>
|
|
216
|
-
<option value="true">Yes</option>
|
|
217
|
-
</select>
|
|
218
|
-
</div>
|
|
219
|
-
</div>
|
|
220
|
-
<button class="solve-btn" id="solveBtn" onclick="solveProblem()">
|
|
221
|
-
Solve Optimization Problem
|
|
222
|
-
</button>
|
|
223
|
-
</div>
|
|
224
|
-
|
|
225
|
-
<div class="card">
|
|
226
|
-
<h2>Results</h2>
|
|
227
|
-
<div class="loading" id="loading">
|
|
228
|
-
<div class="spinner"></div>
|
|
229
|
-
<p>Solving... Please wait</p>
|
|
230
|
-
</div>
|
|
231
|
-
<div class="result" id="result">
|
|
232
|
-
<p style="color: #666;">Results will appear here after solving</p>
|
|
233
|
-
</div>
|
|
234
|
-
</div>
|
|
235
|
-
|
|
236
|
-
<p class="footer">
|
|
237
|
-
Powered by <a href="https://scipopt.org" target="_blank">SCIP Optimization Suite</a> |
|
|
238
|
-
<a href="https://github.com/user/scip.js" target="_blank">GitHub</a>
|
|
239
|
-
</p>
|
|
240
|
-
</div>
|
|
241
|
-
|
|
242
|
-
<script type="module">
|
|
243
|
-
import SCIP from './index.mjs';
|
|
244
|
-
|
|
245
|
-
// Make available globally
|
|
246
|
-
window.SCIP = SCIP;
|
|
247
|
-
|
|
248
|
-
// Example problems
|
|
249
|
-
const examples = {
|
|
250
|
-
lp: `\\ Simple Linear Programming Example
|
|
251
|
-
\\ Minimize production cost
|
|
252
|
-
Minimize
|
|
253
|
-
obj: 2 x1 + 3 x2 + 4 x3
|
|
254
|
-
Subject To
|
|
255
|
-
demand1: x1 + x2 + x3 >= 100
|
|
256
|
-
demand2: 2 x1 + x2 >= 80
|
|
257
|
-
capacity: x1 + 2 x2 + 3 x3 <= 200
|
|
258
|
-
Bounds
|
|
259
|
-
0 <= x1 <= 50
|
|
260
|
-
0 <= x2 <= 60
|
|
261
|
-
0 <= x3 <= 40
|
|
262
|
-
End`,
|
|
263
|
-
|
|
264
|
-
mip: `\\ Mixed Integer Programming Example
|
|
265
|
-
\\ Facility location problem
|
|
266
|
-
Minimize
|
|
267
|
-
obj: 100 y1 + 150 y2 + 80 y3 + 5 x11 + 8 x12 + 6 x13 + 10 x21 + 4 x22 + 9 x23
|
|
268
|
-
Subject To
|
|
269
|
-
\\ Customer demands
|
|
270
|
-
customer1: x11 + x21 >= 50
|
|
271
|
-
customer2: x12 + x22 >= 30
|
|
272
|
-
customer3: x13 + x23 >= 40
|
|
273
|
-
\\ Facility capacities (only if open)
|
|
274
|
-
facility1: x11 + x12 + x13 - 100 y1 <= 0
|
|
275
|
-
facility2: x21 + x22 + x23 - 100 y2 <= 0
|
|
276
|
-
Bounds
|
|
277
|
-
0 <= x11
|
|
278
|
-
0 <= x12
|
|
279
|
-
0 <= x13
|
|
280
|
-
0 <= x21
|
|
281
|
-
0 <= x22
|
|
282
|
-
0 <= x23
|
|
283
|
-
Binary
|
|
284
|
-
y1 y2 y3
|
|
285
|
-
End`,
|
|
286
|
-
|
|
287
|
-
minlp: `\\ Mixed Integer Nonlinear Programming (MINLP) Example
|
|
288
|
-
\\ Portfolio optimization with discrete lot sizes
|
|
289
|
-
\\
|
|
290
|
-
\\ Minimize quadratic risk (variance) with integer constraints
|
|
291
|
-
\\ This is a MIQP (Mixed Integer Quadratic Program)
|
|
292
|
-
Minimize
|
|
293
|
-
obj: [ x1^2 + 2 x2^2 + x1 * x2 ] / 2
|
|
294
|
-
Subject To
|
|
295
|
-
\\ Total investment must be at least 100 units
|
|
296
|
-
budget: x1 + x2 >= 100
|
|
297
|
-
\\ Risk constraint
|
|
298
|
-
risk: x1 - x2 <= 50
|
|
299
|
-
\\ Minimum diversification
|
|
300
|
-
diverse: x2 >= 20
|
|
301
|
-
Bounds
|
|
302
|
-
0 <= x1 <= 200
|
|
303
|
-
0 <= x2 <= 200
|
|
304
|
-
General
|
|
305
|
-
x1
|
|
306
|
-
End`,
|
|
307
|
-
|
|
308
|
-
minlp2: `\\ MINLP: Facility Location with Quadratic Costs
|
|
309
|
-
\\ Open facilities (binary) with nonlinear shipping costs
|
|
310
|
-
Minimize
|
|
311
|
-
obj: 50 open1 + 80 open2 + [ ship1^2 + ship2^2 ] / 2
|
|
312
|
-
Subject To
|
|
313
|
-
\\ Must ship at least 10 units total
|
|
314
|
-
demand: ship1 + ship2 >= 10
|
|
315
|
-
\\ Can only ship from open facilities
|
|
316
|
-
cap1: ship1 - 20 open1 <= 0
|
|
317
|
-
cap2: ship2 - 20 open2 <= 0
|
|
318
|
-
Bounds
|
|
319
|
-
0 <= ship1 <= 20
|
|
320
|
-
0 <= ship2 <= 20
|
|
321
|
-
Binary
|
|
322
|
-
open1 open2
|
|
323
|
-
End`,
|
|
324
|
-
|
|
325
|
-
knapsack: `\\ 0-1 Knapsack Problem
|
|
326
|
-
\\ Select items to maximize value within weight limit
|
|
327
|
-
Maximize
|
|
328
|
-
obj: 60 item1 + 100 item2 + 120 item3 + 80 item4 + 90 item5
|
|
329
|
-
Subject To
|
|
330
|
-
weight: 10 item1 + 20 item2 + 30 item3 + 15 item4 + 25 item5 <= 50
|
|
331
|
-
Binary
|
|
332
|
-
item1 item2 item3 item4 item5
|
|
333
|
-
End`,
|
|
334
|
-
|
|
335
|
-
transportation: `\\ Transportation Problem
|
|
336
|
-
\\ Ship goods from 2 plants to 3 customers at minimum cost
|
|
337
|
-
Minimize
|
|
338
|
-
obj: 8 x11 + 6 x12 + 10 x13 + 9 x21 + 12 x22 + 7 x23
|
|
339
|
-
Subject To
|
|
340
|
-
\\ Supply constraints
|
|
341
|
-
plant1: x11 + x12 + x13 <= 100
|
|
342
|
-
plant2: x21 + x22 + x23 <= 150
|
|
343
|
-
\\ Demand constraints
|
|
344
|
-
cust1: x11 + x21 >= 80
|
|
345
|
-
cust2: x12 + x22 >= 70
|
|
346
|
-
cust3: x13 + x23 >= 60
|
|
347
|
-
Bounds
|
|
348
|
-
x11 >= 0
|
|
349
|
-
x12 >= 0
|
|
350
|
-
x13 >= 0
|
|
351
|
-
x21 >= 0
|
|
352
|
-
x22 >= 0
|
|
353
|
-
x23 >= 0
|
|
354
|
-
End`
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
window.loadExample = function(name) {
|
|
358
|
-
document.getElementById('problem').value = examples[name] || '';
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
window.solveProblem = async function() {
|
|
362
|
-
const problem = document.getElementById('problem').value;
|
|
363
|
-
const timeLimit = parseInt(document.getElementById('timeLimit').value) || 60;
|
|
364
|
-
const gap = parseFloat(document.getElementById('gap').value) / 100 || null;
|
|
365
|
-
const verbose = document.getElementById('verbose').value === 'true';
|
|
366
|
-
|
|
367
|
-
const loading = document.getElementById('loading');
|
|
368
|
-
const result = document.getElementById('result');
|
|
369
|
-
const solveBtn = document.getElementById('solveBtn');
|
|
370
|
-
|
|
371
|
-
loading.classList.add('active');
|
|
372
|
-
result.innerHTML = '';
|
|
373
|
-
solveBtn.disabled = true;
|
|
374
|
-
|
|
375
|
-
try {
|
|
376
|
-
const solution = await SCIP.solve(problem, {
|
|
377
|
-
timeLimit,
|
|
378
|
-
gap,
|
|
379
|
-
verbose
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
displayResult(solution);
|
|
383
|
-
} catch (error) {
|
|
384
|
-
result.innerHTML = `
|
|
385
|
-
<span class="status error">Error</span>
|
|
386
|
-
<pre>${error.message}</pre>
|
|
387
|
-
`;
|
|
388
|
-
} finally {
|
|
389
|
-
loading.classList.remove('active');
|
|
390
|
-
solveBtn.disabled = false;
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
function displayResult(solution) {
|
|
395
|
-
const result = document.getElementById('result');
|
|
396
|
-
|
|
397
|
-
const statusClass = {
|
|
398
|
-
'optimal': 'optimal',
|
|
399
|
-
'infeasible': 'infeasible',
|
|
400
|
-
'unbounded': 'infeasible',
|
|
401
|
-
'timelimit': 'unknown',
|
|
402
|
-
'unknown': 'unknown',
|
|
403
|
-
'error': 'error'
|
|
404
|
-
}[solution.status] || 'unknown';
|
|
405
|
-
|
|
406
|
-
let html = `<span class="status ${statusClass}">${solution.status.toUpperCase()}</span>`;
|
|
407
|
-
|
|
408
|
-
if (solution.status === 'optimal' || solution.objective !== null) {
|
|
409
|
-
html += `
|
|
410
|
-
<div class="result-grid">
|
|
411
|
-
<div class="result-section">
|
|
412
|
-
<h3>Objective Value</h3>
|
|
413
|
-
<pre>${solution.objective}</pre>
|
|
414
|
-
|
|
415
|
-
<h3>Variables</h3>
|
|
416
|
-
<pre>${JSON.stringify(solution.variables, null, 2)}</pre>
|
|
417
|
-
</div>
|
|
418
|
-
<div class="result-section">
|
|
419
|
-
<h3>Statistics</h3>
|
|
420
|
-
<pre>${JSON.stringify(solution.statistics, null, 2)}</pre>
|
|
421
|
-
</div>
|
|
422
|
-
</div>
|
|
423
|
-
`;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
if (solution.error) {
|
|
427
|
-
html += `<pre style="color: #da3633;">${solution.error}</pre>`;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
result.innerHTML = html;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// Load LP example by default
|
|
434
|
-
loadExample('lp');
|
|
435
|
-
|
|
436
|
-
// Initialize SCIP
|
|
437
|
-
console.log('Initializing SCIP.js...');
|
|
438
|
-
SCIP.init().then(() => {
|
|
439
|
-
console.log('SCIP.js ready!');
|
|
440
|
-
}).catch(err => {
|
|
441
|
-
console.error('Failed to initialize SCIP:', err);
|
|
442
|
-
});
|
|
443
|
-
</script>
|
|
444
|
-
</body>
|
|
445
|
-
</html>
|
package/dist/test-browser.html
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
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 Browser Bundle Test</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
|
-
}
|
|
23
|
-
.success { color: #4caf50; }
|
|
24
|
-
.error { color: #f44336; }
|
|
25
|
-
button {
|
|
26
|
-
background: #4fc3f7;
|
|
27
|
-
color: #1a1a2e;
|
|
28
|
-
border: none;
|
|
29
|
-
padding: 10px 20px;
|
|
30
|
-
border-radius: 6px;
|
|
31
|
-
cursor: pointer;
|
|
32
|
-
font-size: 16px;
|
|
33
|
-
margin: 10px 5px 10px 0;
|
|
34
|
-
}
|
|
35
|
-
button:hover { background: #29b6f6; }
|
|
36
|
-
#result { margin-top: 20px; }
|
|
37
|
-
</style>
|
|
38
|
-
</head>
|
|
39
|
-
<body>
|
|
40
|
-
<h1>SCIP.js Browser Bundle Test</h1>
|
|
41
|
-
<p>Testing the IIFE bundle loaded via <code><script></code> tag (no type="module")</p>
|
|
42
|
-
|
|
43
|
-
<button onclick="testSCIP()">Run Test</button>
|
|
44
|
-
<button onclick="checkGlobal()">Check Global SCIP</button>
|
|
45
|
-
|
|
46
|
-
<div id="result">
|
|
47
|
-
<p>Click "Run Test" to solve a simple LP problem</p>
|
|
48
|
-
</div>
|
|
49
|
-
|
|
50
|
-
<!-- Load the browser bundle WITHOUT type="module" -->
|
|
51
|
-
<script src="scip.min.js"></script>
|
|
52
|
-
|
|
53
|
-
<script>
|
|
54
|
-
// This should work because SCIP is exposed globally
|
|
55
|
-
|
|
56
|
-
function log(msg, isError = false) {
|
|
57
|
-
const result = document.getElementById('result');
|
|
58
|
-
result.innerHTML += `<pre class="${isError ? 'error' : ''}">${msg}</pre>`;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function checkGlobal() {
|
|
62
|
-
const result = document.getElementById('result');
|
|
63
|
-
result.innerHTML = '';
|
|
64
|
-
|
|
65
|
-
if (typeof SCIP !== 'undefined') {
|
|
66
|
-
log('window.SCIP exists: ' + JSON.stringify(Object.keys(SCIP), null, 2), false);
|
|
67
|
-
} else {
|
|
68
|
-
log('ERROR: window.SCIP is undefined!', true);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async function testSCIP() {
|
|
73
|
-
const result = document.getElementById('result');
|
|
74
|
-
result.innerHTML = '<p>Running...</p>';
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
// Check if SCIP is available
|
|
78
|
-
if (typeof SCIP === 'undefined') {
|
|
79
|
-
throw new Error('SCIP is not defined. Bundle may not have loaded correctly.');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
log('SCIP object found. Available methods: ' + Object.keys(SCIP).join(', '));
|
|
83
|
-
|
|
84
|
-
// Initialize SCIP
|
|
85
|
-
log('Initializing SCIP...');
|
|
86
|
-
await SCIP.init({ wasmPath: './scip.wasm' });
|
|
87
|
-
log('SCIP initialized successfully!');
|
|
88
|
-
|
|
89
|
-
// Solve a simple LP problem
|
|
90
|
-
const problem = `
|
|
91
|
-
Minimize
|
|
92
|
-
obj: 2 x + 3 y
|
|
93
|
-
Subject To
|
|
94
|
-
c1: x + y >= 4
|
|
95
|
-
c2: 2 x + y >= 5
|
|
96
|
-
Bounds
|
|
97
|
-
x >= 0
|
|
98
|
-
y >= 0
|
|
99
|
-
End
|
|
100
|
-
`;
|
|
101
|
-
|
|
102
|
-
log('Solving LP problem...');
|
|
103
|
-
const solution = await SCIP.solve(problem);
|
|
104
|
-
|
|
105
|
-
log('<span class="success">Solution found!</span>');
|
|
106
|
-
log('Status: ' + solution.status);
|
|
107
|
-
log('Objective: ' + solution.objective);
|
|
108
|
-
log('Variables: ' + JSON.stringify(solution.variables, null, 2));
|
|
109
|
-
log('Statistics: ' + JSON.stringify(solution.statistics, null, 2));
|
|
110
|
-
|
|
111
|
-
} catch (error) {
|
|
112
|
-
log('Error: ' + error.message, true);
|
|
113
|
-
console.error(error);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
</script>
|
|
117
|
-
</body>
|
|
118
|
-
</html>
|
package/dist/test-worker.html
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
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>
|