@areb0s/scip.js 1.1.7 → 1.1.9

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.
@@ -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 = output.match(/objective value:\s*([\d.e+-]+)/i);
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
- // Format: variable_name value (obj:coef)
104
- const varRegex = /^(\w+)\s+([\d.e+-]+)/gm;
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
- // Look for solution section
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
- // Parse results
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/dist/scip.js CHANGED
@@ -257,9 +257,11 @@ var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;Module["ready"]=
257
257
  };
258
258
 
259
259
  } catch (error) {
260
+ // Handle both Error objects and raw values (Emscripten can throw numbers/strings)
261
+ var errorMsg = error instanceof Error ? error.message : String(error);
260
262
  return {
261
263
  status: Status.ERROR,
262
- error: error.message,
264
+ error: errorMsg,
263
265
  output: stdout + stderr
264
266
  };
265
267
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@areb0s/scip.js",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "description": "SCIP Optimization Solver compiled to WebAssembly - LP, MIP, and MINLP support",
5
5
  "main": "dist/index.mjs",
6
6
  "module": "dist/index.mjs",
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>
@@ -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>&lt;script&gt;</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>
@@ -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>