Skip to content

Commit

Permalink
Further splitting up readNonMatForm()
Browse files Browse the repository at this point in the history
  • Loading branch information
fusion809 committed Nov 21, 2020
1 parent c7c2b8a commit e5cde8f
Show file tree
Hide file tree
Showing 3 changed files with 373 additions and 315 deletions.
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<script src="eqns.js"></script>
<script src="alternateSoln.js"></script>
<script src="permDegn.js"></script>
<script src="readNonMat.js"></script>
<link rel="icon" href="/assets/favicon.png">
</head>

Expand Down
315 changes: 0 additions & 315 deletions readInputs.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,321 +86,6 @@ function readInputs() {
return [A, b, cj, x, xB];
}

/**
* Read problem in non-matrix form.
*
* @params None, reads inputs from "nonMatForm" textarea.
* @return [A, b, cj, x, xB, shouldDie, sign, varName]
*/
function readNonMatForm() {
// Element of the text area
var element = document.getElementById("nonMatForm").value;
element = element.replace(//g, ">=").replace(//g, "<=");

// Create KaTeX string
var texStr = "\\begin{aligned}\n";
var stStr = "\\\\\n&\\mathrm{Subject\\hspace{0.1cm}to:}\\\\\n&";

// Regular expressions
var dnReg = new RegExp(/(\d)(\n)/gi);
var ldReg = new RegExp(/([a-zA-Z])(\d+)/gi);
var maxReg = new RegExp(/[mM]ax[imize]*[imise]*/);
var minReg = new RegExp(/[mM]in[imize]*[imise]*/);
var midLnReg = new RegExp(/\n[a-zA-Z .:]*\n/);
var decVarRegg = new RegExp(/[a-zA-Z][a-zA-Z]*[0-9]*/g);

// Type of objective function
var maxStr = "&\\mathrm{Maximize\\hspace{0.1cm}}";
var minStr = "&\\mathrm{Minimize\\hspace{0.1cm}}";

// Replace blank/subject to lines with "Subject to:" with TeX formatting
texStr += element.replace(midLnReg, stStr);
texStr = texStr.replace(dnReg,
(match, number) => number + "\\\\\n&");
texStr = texStr.replace(ldReg,
(match, letter, number) => `${letter}_${number}`);
texStr = texStr.replace(/<=/g, "&&\\leq").replace(/>=/g, "&&\\geq");
texStr = texStr.replace(/(?![<>])=/g, "&&=");
texStr = texStr.replace(maxReg, maxStr);
texStr = texStr.replace(minReg, minStr);

// Deal with &geq; and &leq; chars
var elSpArr = element.split(" ");
var elNLArr = element.split("\n");

// Type is = 1 if max problem, -1 otherwise
var type = elSpArr[0];

// Right-hand side of objective function
var objRHS = elNLArr[0].replace(/.*=/, '').replace(/ /g, "");

// Extract all decision variables from objective function
var x = objRHS.match(decVarRegg).join(" ").split(" ");
var varNo = x.length;

// Non-negativity constraints
texStr += "\\\\\n";
texStr += "&";
for (let i = 0; i < varNo; i++) {
texStr += x[i].replace(/\d+/, function(x) {
return "_{" + x + "}";
});
if (i < varNo - 1) {
texStr += ",\\hspace{0.1cm}";
} else {
texStr += " &&\\geq 0."
}
}

// End TeX string
texStr += "\n\\end{aligned}";

// Write texStr to tempStr
if (dualCheck) {
tempStr += "<br/>Solving the dual.<br/><br/>";
}
tempStr += "Problem is:<br/>"
tempStr += "<span class=\"katex-display\">";
tempStr += katex.renderToString(texStr);
tempStr += "</span>";

// Name of the objective function (e.g. z)
var objVarName = elSpArr[1].replace(/=/, '');

if (type.match(maxReg) ) {
type = 1;
} else if (type.match(minReg)) {
type = -1;
tempStr += "Multiplying ";
tempStr += objVarName;
tempStr += " by -1 to get a maximization problem.<br/><br/>";
} else {
var msg = "Odd, your problem doesn't seem to be either a maximization";
msg += " or minimization problem";
console.error(msg);
}

// Initialize cj
var cj = [];

// Array of signs for objective function coefficients
var signRHSArr = objRHS.match(/[+-]/g).join("");

// Array of coefficients (w/o sign) with variables they correspond to
var coeffsArr = objRHS.split(/[+-]/);
// Remove any empty elements from array
var coeffsArr = coeffsArr.filter(function(el) {
return el != "";
});

// Count up number of less than or equal to inequalities
var elMLeq = element.match(/<=/g);
if (elMLeq) {
var noOfLeq = elMLeq.join("").replace(/</g, "").length;
} else {
var noOfLeq = 0;
}

// Count up number of greater than or equal to inequalities
var elMGeq = element.match(/>=/g);
if (elMGeq) {
var noOfGeq = elMGeq.join("").replace(/>/g, "").length;
} else {
var noOfGeq = 0;
}

// Count up equalities
var elMEq = element.match(/ =/g);
if (elMEq) {
var noOfEq = 2*(elMEq.join("").replace(/ /g, "").length-1);
} else {
var noOfEq = 0;
}

// For some reason the following calculation causes us to access a
// non-existent element of elNLArr below
var noOfConstr = noOfLeq + noOfGeq + noOfEq;
var decVarReg = new RegExp(/[a-zA-Z][a-zA-Z]*[0-9]*/);

// Loop over objective function coefficients array
for (let i = 0; i < varNo; i++) {
if (coeffsArr[i] != "") {
var coeffWOSign = coeffsArr[i].replace(decVarReg, '');

// If number is omitted, the coeff = 1
if (!coeffWOSign.match(/[0-9]/)) {
coeffWOSign += "1";
}

// First element, if there's no sign is given
if ( (varNo == signRHSArr.length + 1) && (i == 0)) {
var coeff = fracToDecimal(coeffWOSign);
}
// Other elements, if no sign is given for first element
else if (varNo == signRHSArr.length + 1) {
// This first line sets the sign of the coefficient
var coeff = parseInt(signRHSArr[i-1] + "1");
coeff *= fracToDecimal(coeffWOSign);
}
// Elements for rows where every sign is explicity specified
else {
// This first line sets the sign of the coefficient
var coeff = parseInt(signRHSArr[i] + "1");
coeff *= fracToDecimal(coeffWOSign);
}

// Add to cj
cj.push(type*coeff);
}
}

// Initialize arrays
var A = new Array(noOfConstr);
var xB = new Array(noOfConstr);
var b = new Array(noOfConstr);

// Number of columns in A
var mn = varNo + noOfConstr;

// Counter variables
var j = 0;
var countOfEq = 0;

// Determine number of empty rows
var noOfEmptyRows = 0;
for (let i = 0 ; i < elNLArr.length; i++) {
var line = elNLArr[i];
var isBlankLn = line.match(/^\s*$/);
var isStLn = line.match(/[Ss][ubject to|t.][:]*/);

// Class either blank lines or st. lines as empty rows
if (isBlankLn || isStLn) {
noOfEmptyRows++;
}
}

// Loop over constraints
while (j < noOfConstr) {
// Relevant vars
var constrLn = elNLArr[j+1+noOfEmptyRows-countOfEq];
var isConstrEq = constrLn.match(/ =/);
var isConstrLeq = constrLn.match(/<=/);
var isConstrGeq = constrLn.match(/>=/);

// Initialize relevant rows
if (isConstrEq) {
A[j+1] = new Array(mn);
}
A[j] = new Array(mn);

// Add slack entries
cj.push(0);

// LHS of constraint
var constrWoSp = constrLn.replace(/ /g, "");
var constrLHS = constrWoSp.replace(/[<>]*=.*/, '');
var constrRHS = constrWoSp.replace(/.*=/, '');

// RHS of constraint
var resc = fracToDecimal(constrRHS);

// Detail what is being done before initial tableau is drawn
if (isConstrEq) {
tempStr += "Splitting constraint ";
tempStr += (j+1 - countOfEq);
tempStr += " into &leq; and &geq; constraints. &geq; constraint ";
tempStr += "must be multiplied by -1 to turn it into a &leq; ";
tempStr += "constraint so it can then be converted to canonical ";
tempStr += "form and added to the initial tableau."
tempStr += "<br/><br/> ";
b[j] = resc;
b[j+1] = -resc;
} else if (isConstrLeq) {
b[j] = resc;
} else {
tempStr += "Multiplying constraint ";
tempStr += (j+1-countOfEq);
tempStr += " by -1 to replace &geq; with &leq;, which can then be";
tempStr += " converted to canonical form and added to the initial";
tempStr += " tableau.<br/><br/>";
b[j] = -resc;
}

// Loop over every variable, determine coefficient and add to A
for (let i = 0; i < varNo; i++) {

// Obtain coeff of x[i] for constraint j
var varName = x[i];
var regex = new RegExp(`[+-]*[0-9/]*${varName}`);
if (constrLHS.match(regex)) {
var coeff = constrLHS.match(regex).join("").replace(varName, "");
if (!coeff.match(/[0-9]/)) {
coeff += "1";
}
coeff = fracToDecimal(coeff);
} else {
var coeff = 0;
}

// Add coeffs to A
if (isConstrLeq) {
A[j][i] = coeff;
} else if (isConstrGeq) {
A[j][i] = -coeff;
} else if (isConstrEq) {
A[j][i] = coeff;
A[j+1][i] = -coeff;
}
}

// Loop over slack variable columns
for (let k = varNo ; k < mn; k++) {
// Slack variable for constraint
if (j == k - varNo) {
A[j][k] = 1;
} else {
A[j][k] = 0;
}

// Add slack variables in extra row if constraint is an equality
if (isConstrEq) {
if (j+1 == k - varNo) {
A[j+1][k] = 1;
} else {
A[j+1][k] = 0;
}
}
}

// Add slacks to x and xB
var sj = "s" + (j+1) + dualDash(dualCheck);
xB[j] = sj;
x.push(sj);

// If constraint is an equality, add additional entries to cj, x and
// xB, increment j by 2 and countOfEq by 1.
// Otherwise just increment j by 2.
if (isConstrEq) {
// Second split constraint slack variable
var sj1 = "s" + (j+2) + dualDash(dualCheck);
xB[j+1] = sj1;
x.push(sj1);
cj.push(0);

// Incrementing by 2 constraint counter
j += 2;

// Increment equality constraint count
countOfEq++;
} else {
j++;
}
}

// Return all data needed by getParameters()
return [A, b, cj, x, xB, false, type, objVarName];
}

/**
* Uncheck specified radio button if it is checked.
*
Expand Down
Loading

0 comments on commit e5cde8f

Please sign in to comment.