@areb0s/scip.js 1.1.5 → 1.1.7

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,32 +88,29 @@ 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)
93
91
  */
94
- function parseSolution(output, rawSolution = null) {
92
+ function parseSolution(output) {
95
93
  const variables = {};
96
94
  const objective = { value: null, sense: null };
97
95
 
98
- // Use rawSolution if available (more reliable)
99
- const solText = rawSolution || output;
100
-
101
96
  // Parse objective value
102
- const objMatch = solText.match(/objective value:\s*([\d.e+-]+)/i);
97
+ const objMatch = output.match(/objective value:\s*([\d.e+-]+)/i);
103
98
  if (objMatch) {
104
99
  objective.value = parseFloat(objMatch[1]);
105
100
  }
106
101
 
107
102
  // Parse variable values from solution display
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;
103
+ // Format: variable_name value (obj:coef)
104
+ const varRegex = /^(\w+)\s+([\d.e+-]+)/gm;
111
105
  let match;
112
106
 
113
- while ((match = varRegex.exec(solText)) !== null) {
107
+ // Look for solution section
108
+ const solSection = output.split('solution:')[1] || output;
109
+
110
+ while ((match = varRegex.exec(solSection)) !== null) {
114
111
  const name = match[1];
115
112
  const value = parseFloat(match[2]);
116
- if (!isNaN(value) && name !== 'objective' && name !== 'solution') {
113
+ if (!isNaN(value) && name !== 'objective') {
117
114
  variables[name] = value;
118
115
  }
119
116
  }
@@ -332,7 +329,12 @@ export async function solve(problem, options = {}) {
332
329
  // Run SCIP with batch mode
333
330
  const exitCode = scipModule.callMain(['-b', '/settings/commands.txt']);
334
331
 
335
- // Try to read solution file first (more reliable for parsing)
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
336
338
  let rawSolution = null;
337
339
  try {
338
340
  rawSolution = scipModule.FS.readFile(solutionFile, { encoding: 'utf8' });
@@ -340,11 +342,6 @@ export async function solve(problem, options = {}) {
340
342
  // Solution file may not exist if infeasible
341
343
  }
342
344
 
343
- // Parse results
344
- const status = parseStatus(stdout);
345
- const { variables, objective } = parseSolution(stdout, rawSolution);
346
- const statistics = parseStatistics(stdout);
347
-
348
345
  return {
349
346
  status,
350
347
  objective: objective.value,
package/dist/scip.js CHANGED
@@ -1,28 +1,4 @@
1
1
 
2
- /**
3
- * SCIP.js Browser Bundle
4
- * Supports: LP, MIP, MINLP (Mixed Integer Nonlinear Programming)
5
- */
6
- (function(global) {
7
- 'use strict';
8
-
9
- // Script directory detection for WASM loading
10
- var __SCIP_SCRIPT_DIR__ = (function() {
11
- // Check for explicit SCIP_BASE_URL
12
- if (typeof SCIP_BASE_URL !== 'undefined' && SCIP_BASE_URL) {
13
- return SCIP_BASE_URL + (SCIP_BASE_URL.endsWith('/') ? '' : '/');
14
- }
15
- // Try to detect from current script
16
- if (typeof document !== 'undefined' && document.currentScript && document.currentScript.src) {
17
- var src = document.currentScript.src;
18
- return src.substring(0, src.lastIndexOf('/') + 1);
19
- }
20
- // Default CDN (npm)
21
- return 'https://cdn.jsdelivr.net/npm/@areb0s/scip.js@latest/dist/';
22
- })();
23
-
24
- // Inline the transformed scip-core.js (createSCIP factory function)
25
-
26
2
  /**
27
3
  * SCIP.js Browser Bundle
28
4
  * Supports: LP, MIP, MINLP (Mixed Integer Nonlinear Programming)
@@ -346,291 +322,4 @@ var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;Module["ready"]=
346
322
  console.error('[SCIP.js] Auto-initialization failed:', err.message);
347
323
  });
348
324
 
349
- })(typeof self !== 'undefined' ? self : typeof window !== 'undefined' ? window : globalThis);
350
-
351
-
352
- // SCIP wrapper implementation
353
- var scipModule = null;
354
- var isInitialized = false;
355
- var initPromise = null;
356
- var readyResolve = null;
357
- var readyReject = null;
358
-
359
- var readyPromise = new Promise(function(resolve, reject) {
360
- readyResolve = resolve;
361
- readyReject = reject;
362
- });
363
-
364
- var Status = {
365
- OPTIMAL: 'optimal',
366
- INFEASIBLE: 'infeasible',
367
- UNBOUNDED: 'unbounded',
368
- TIME_LIMIT: 'timelimit',
369
- UNKNOWN: 'unknown',
370
- ERROR: 'error'
371
- };
372
-
373
- function parseStatus(output) {
374
- if (output.includes('optimal solution found')) return Status.OPTIMAL;
375
- if (output.includes('problem is infeasible')) return Status.INFEASIBLE;
376
- if (output.includes('problem is unbounded')) return Status.UNBOUNDED;
377
- if (output.includes('time limit reached')) return Status.TIME_LIMIT;
378
- return Status.UNKNOWN;
379
- }
380
-
381
- function parseSolution(output, rawSolution) {
382
- var variables = {};
383
- var objective = { value: null };
384
-
385
- // Use rawSolution if available (more reliable)
386
- var solText = rawSolution || output;
387
-
388
- var objMatch = solText.match(/objective value:\s*([\d.e+-]+)/i);
389
- if (objMatch) {
390
- objective.value = parseFloat(objMatch[1]);
391
- }
392
-
393
- // Match ZIMPL-style variable names: x$sun#0, effSum$star#1, b_sun_10, etc.
394
- // Format: variableName value (obj:coef)
395
- var varRegex = /^([\w$#]+)\s+([\d.e+-]+)/gm;
396
- var match;
397
-
398
- while ((match = varRegex.exec(solText)) !== null) {
399
- var name = match[1];
400
- var value = parseFloat(match[2]);
401
- if (!isNaN(value) && name !== 'objective' && name !== 'solution') {
402
- variables[name] = value;
403
- }
404
- }
405
-
406
- return { variables: variables, objective: objective };
407
- }
408
-
409
- function parseStatistics(output) {
410
- var stats = { solvingTime: null, nodes: null, iterations: null, gap: null };
411
-
412
- var timeMatch = output.match(/Solving Time \(sec\)\s*:\s*([\d.]+)/);
413
- if (timeMatch) stats.solvingTime = parseFloat(timeMatch[1]);
414
-
415
- var nodesMatch = output.match(/Nodes\s*:\s*(\d+)/);
416
- if (nodesMatch) stats.nodes = parseInt(nodesMatch[1]);
417
-
418
- var iterMatch = output.match(/LP Iterations\s*:\s*(\d+)/);
419
- if (iterMatch) stats.iterations = parseInt(iterMatch[1]);
420
-
421
- var gapMatch = output.match(/Gap\s*:\s*([\d.]+)\s*%/);
422
- if (gapMatch) stats.gap = parseFloat(gapMatch[1]);
423
-
424
- return stats;
425
- }
426
-
427
- function init(options) {
428
- options = options || {};
429
-
430
- if (isInitialized) {
431
- return Promise.resolve();
432
- }
433
- if (initPromise) {
434
- return initPromise;
435
- }
436
-
437
- initPromise = new Promise(function(resolve, reject) {
438
- try {
439
- var wasmPath = options.wasmPath || (__SCIP_SCRIPT_DIR__ + 'scip.wasm');
440
-
441
- createSCIP({
442
- locateFile: function(path) {
443
- if (path.endsWith('.wasm')) {
444
- return wasmPath;
445
- }
446
- return path;
447
- },
448
- print: function(text) {
449
- if (scipModule && scipModule.onStdout) {
450
- scipModule.onStdout(text);
451
- }
452
- },
453
- printErr: function(text) {
454
- if (scipModule && scipModule.onStderr) {
455
- scipModule.onStderr(text);
456
- }
457
- }
458
- }).then(function(module) {
459
- scipModule = module;
460
-
461
- if (scipModule.FS) {
462
- try { scipModule.FS.mkdir('/problems'); } catch (e) {}
463
- try { scipModule.FS.mkdir('/solutions'); } catch (e) {}
464
- try { scipModule.FS.mkdir('/settings'); } catch (e) {}
465
- }
466
-
467
- isInitialized = true;
468
- if (readyResolve) readyResolve();
469
- resolve();
470
- }).catch(function(err) {
471
- var error = new Error('SCIP WASM loading failed: ' + (err.message || err));
472
- if (readyReject) readyReject(error);
473
- reject(error);
474
- });
475
- } catch (err) {
476
- if (readyReject) readyReject(err);
477
- reject(err);
478
- }
479
- });
480
-
481
- return initPromise;
482
- }
483
-
484
- function solve(problem, options) {
485
- options = options || {};
486
-
487
- var doSolve = function() {
488
- var format = options.format || 'lp';
489
- var timeLimit = options.timeLimit || 3600;
490
- var gap = options.gap || null;
491
- var verbose = options.verbose || false;
492
- var parameters = options.parameters || {};
493
-
494
- var stdout = '';
495
- var stderr = '';
496
-
497
- scipModule.onStdout = function(text) {
498
- stdout += text + '\n';
499
- if (verbose) console.log('[SCIP]', text);
500
- };
501
-
502
- scipModule.onStderr = function(text) {
503
- stderr += text + '\n';
504
- if (verbose) console.error('[SCIP Error]', text);
505
- };
506
-
507
- try {
508
- var formatExtMap = { mps: 'mps', zpl: 'zpl', cip: 'cip', lp: 'lp' };
509
- var ext = formatExtMap[format] || 'lp';
510
- var problemFile = '/problems/problem.' + ext;
511
- var solutionFile = '/solutions/solution.sol';
512
-
513
- scipModule.FS.writeFile(problemFile, problem);
514
-
515
- var commands = [];
516
- commands.push('set limits time ' + timeLimit);
517
-
518
- if (gap !== null) {
519
- commands.push('set limits gap ' + gap);
520
- }
521
-
522
- for (var key in parameters) {
523
- if (parameters.hasOwnProperty(key)) {
524
- commands.push('set ' + key + ' ' + parameters[key]);
525
- }
526
- }
527
-
528
- commands.push('read ' + problemFile);
529
- commands.push('optimize');
530
- commands.push('display solution');
531
- commands.push('write solution ' + solutionFile);
532
- commands.push('display statistics');
533
- commands.push('quit');
534
-
535
- var settingsContent = commands.join('\n');
536
- scipModule.FS.writeFile('/settings/commands.txt', settingsContent);
537
-
538
- var exitCode = scipModule.callMain(['-b', '/settings/commands.txt']);
539
-
540
- // Read rawSolution first (more reliable for parsing)
541
- var rawSolution = null;
542
- try {
543
- rawSolution = scipModule.FS.readFile(solutionFile, { encoding: 'utf8' });
544
- } catch (e) {}
545
-
546
- var status = parseStatus(stdout);
547
- var parsed = parseSolution(stdout, rawSolution);
548
- var statistics = parseStatistics(stdout);
549
-
550
- // Cleanup
551
- var cleanupFiles = [
552
- '/problems/problem.lp', '/problems/problem.mps',
553
- '/problems/problem.zpl', '/problems/problem.cip',
554
- '/solutions/solution.sol', '/settings/commands.txt'
555
- ];
556
- for (var i = 0; i < cleanupFiles.length; i++) {
557
- try { scipModule.FS.unlink(cleanupFiles[i]); } catch (e) {}
558
- }
559
-
560
- return {
561
- status: status,
562
- objective: parsed.objective.value,
563
- variables: parsed.variables,
564
- statistics: statistics,
565
- exitCode: exitCode,
566
- output: verbose ? stdout : undefined,
567
- rawSolution: rawSolution
568
- };
569
-
570
- } catch (error) {
571
- return {
572
- status: Status.ERROR,
573
- error: error.message,
574
- output: stdout + stderr
575
- };
576
- }
577
- };
578
-
579
- if (!isInitialized) {
580
- return init(options).then(doSolve);
581
- }
582
- return Promise.resolve(doSolve());
583
- }
584
-
585
- function minimize(problem, options) {
586
- if (!problem.toLowerCase().includes('minimize')) {
587
- problem = 'Minimize\n' + problem;
588
- }
589
- return solve(problem, options);
590
- }
591
-
592
- function maximize(problem, options) {
593
- if (!problem.toLowerCase().includes('maximize')) {
594
- problem = 'Maximize\n' + problem;
595
- }
596
- return solve(problem, options);
597
- }
598
-
599
- function version() {
600
- var getVersion = function() {
601
- var output = '';
602
- scipModule.onStdout = function(text) { output += text + '\n'; };
603
- scipModule.callMain(['--version']);
604
- return output.trim();
605
- };
606
-
607
- if (!isInitialized) {
608
- return init().then(getVersion);
609
- }
610
- return Promise.resolve(getVersion());
611
- }
612
-
613
- function isReady() {
614
- return isInitialized;
615
- }
616
-
617
- // Expose SCIP API
618
- var SCIP = {
619
- init: init,
620
- ready: readyPromise,
621
- isReady: isReady,
622
- solve: solve,
623
- minimize: minimize,
624
- maximize: maximize,
625
- version: version,
626
- Status: Status
627
- };
628
-
629
- global.SCIP = SCIP;
630
-
631
- // Auto-initialize
632
- init().catch(function(err) {
633
- console.error('[SCIP.js] Auto-initialization failed:', err.message);
634
- });
635
-
636
325
  })(typeof self !== 'undefined' ? self : typeof window !== 'undefined' ? window : globalThis);