wow this actually works

main
parent 8b29e8cab6
commit 1c119d1250

@ -0,0 +1,14 @@
export const MiniMark = ({ content }: { content: string }) => {
return (
<p
dangerouslySetInnerHTML={{
__html: content
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\\n/g, '<br>')
.replace(/\*(.*?)\*/g, '<strong>$1</strong>')
.replace(/_(.*?)_/g, '<em>$1</em>'),
}}
/>
)
}

@ -1,42 +1,128 @@
import { Katex } from './Katex'
import { drawSemiplane } from './lib-v2/canvas'
import { indexSetToLatex, matrixToLatex, rowVectorToLatex, vectorToLatex } from './lib-v2/latex'
import { fillDot, drawSemiplane, drawSimpleArrow } from './lib-v2/canvas'
import { range } from './lib-v2/math'
import { Matrix } from './lib-v2/matrix'
import { Rational, RationalField } from './lib-v2/rationals'
import { Rational } from './lib-v2/rationals'
import { ProblemComment, computePrimalSimplexSteps } from './lib-v2/ro/primal-simplex'
import { Vector } from './lib-v2/vector'
import { MiniMark } from './MiniMark'
import { ProblemInput } from './parser-problem'
type Step = {
// type Step = {
// B: number[]
// }
// const activeIndices = (input: ProblemInput, x: Vector<Rational>): number[] => {
// const { A, b } = input
// const A_x = A.apply(x)
// return A_x.getData().flatMap((a, i) => (a.eq(b.at(i)) ? [i] : []))
// }
const PrimalStep = ({
iter,
A,
b,
c,
B,
x,
xi,
comments,
}: {
iter: number
A: Matrix<Rational>
b: Vector<Rational>
c: Vector<Rational>
B: number[]
}
x?: Vector<Rational>
xi?: Vector<Rational>
const activeIndices = (input: ProblemInput, x: Vector<Rational>): number[] => {
const { A, b } = input
const A_x = A.apply(x)
comments: ProblemComment[]
}) => {
return (
<div class="step">
<div class="algebraic-step">
<div class="row">
<p>
Iterazione <strong>{iter + 1}</strong> dell'algoritmo
</p>
</div>
return A_x.getData().flatMap((a, i) => (a.eq(b.at(i)) ? [i] : []))
{comments.map(comment =>
comment.type === 'formula' ? (
<div class="row">
<Katex formula={comment.content} />
</div>
) : (
<div class="row">
<MiniMark content={comment.content} />
</div>
)
)}
</div>
<div class="geometric-step">
<PrimalCanvas
{...{
A,
b,
c,
B,
x,
xi,
}}
/>
</div>
</div>
)
}
export const Primale = ({ input }: { input: ProblemInput }) => {
const steps: Step[] = [{ B: input.B }]
// const steps: Step[] = [{ B: input.B }]
const problemOutput = computePrimalSimplexSteps({
A: input.A,
b: input.b,
c: input.c,
B: input.B,
maxIterations: 10,
})
return (
<div class="steps">
{steps.map(step => (
<PrimaleStep input={input} step={step} />
{problemOutput.steps.map((step, iter) => (
<PrimalStep
{...{
iter,
A: input.A,
b: input.b,
c: input.c,
B: step.B,
x: step.x,
xi: step.xi,
comments: step.comments,
}}
/>
))}
</div>
)
}
const PrimaleCanvas = ({
const PrimalCanvas = ({
A,
b,
c,
B,
x,
xi,
}: {
A: Matrix<Rational>
b: Vector<Rational>
@ -44,6 +130,7 @@ const PrimaleCanvas = ({
B: number[]
x?: Vector<Rational>
xi?: Vector<Rational>
}) => {
const render = ($canvas: HTMLCanvasElement | null) => {
if (!$canvas) {
@ -67,23 +154,61 @@ const PrimaleCanvas = ({
g.lineCap = 'round'
g.lineJoin = 'round'
// draw y axis arrow
g.beginPath()
g.moveTo(width / 2, height / 2)
g.lineTo(width / 2, 5)
g.lineTo(width / 2 - 10, 15)
g.moveTo(width / 2, 5)
g.lineTo(width / 2 + 10, 15)
g.stroke()
// draw x axis arrow
g.beginPath()
g.moveTo(width / 2, height / 2)
g.lineTo(width - 5, height / 2)
g.lineTo(width - 15, height / 2 - 10)
g.moveTo(width - 5, height / 2)
g.lineTo(width - 15, height / 2 + 10)
g.stroke()
g.fillStyle = '#333'
g.font = 'bold 16px sans-serif'
g.textAlign = 'center'
g.textBaseline = 'middle'
// // draw y axis arrow
// g.beginPath()
// g.moveTo(width / 2, height / 2)
// g.lineTo(width / 2, 5)
// g.lineTo(width / 2 - 10, 15)
// g.moveTo(width / 2, 5)
// g.lineTo(width / 2 + 10, 15)
// g.stroke()
// // draw x axis arrow
// g.beginPath()
// g.moveTo(width / 2, height / 2)
// g.lineTo(width - 5, height / 2)
// g.lineTo(width - 15, height / 2 - 10)
// g.moveTo(width - 5, height / 2)
// g.lineTo(width - 15, height / 2 + 10)
// g.stroke()
// draw c vector
const [c1, c2] = c.getData()
const cLen = Math.sqrt(c1.toNumber() ** 2 + c2.toNumber() ** 2)
// g.save()
g.strokeStyle = 'darkgreen'
g.lineWidth = 2
drawSimpleArrow(
g,
50,
height - 50,
50 + (c1.toNumber() / cLen) * 40,
height - 50 - (c2.toNumber() / cLen) * 40,
5
)
g.fillStyle = 'darkgreen'
fillDot(g, 50, height - 50, 4)
g.fillText(`c`, 50 - (c1.toNumber() / cLen) * 20, height - 50 + (c2.toNumber() / cLen) * 20)
// g.beginPath()
// g.translate(50, height - 50)
// g.rotate(Math.atan2(c2.toNumber(), c1.toNumber()))
// g.moveTo(0, 0)
// g.lineTo(30, 0)
// g.moveTo(30, 0)
// g.lineTo(25, -5)
// g.moveTo(30, 0)
// g.lineTo(25, 5)
// g.stroke()
// g.restore()
// g.fillStyle = '#333'
// g.font = '16px sans-serif'
@ -108,232 +233,263 @@ const PrimaleCanvas = ({
})
// draw semiplanes in B
B.forEach(i => {
const [a1, a2] = A.rowAt(i).getData()
const b_i = b.at(i)
drawSemiplane(g, a1.toNumber(), a2.toNumber(), b_i.toNumber(), { lineColor: '#040', lineWidth: 3 })
drawSemiplane(g, a1.toNumber(), a2.toNumber(), b_i.toNumber(), {
lineColor: '#040',
lineWidth: 3,
})
})
// draw x
// draw current solution
if (x) {
const [x1, x2] = x.getData()
g.fillStyle = '#f00'
g.beginPath()
g.ellipse(x1.toNumber(), x2.toNumber(), 0.1, 0.1, 0, 0, 2 * Math.PI)
g.fill()
}
}
return <canvas ref={render} />
}
export const PrimaleStep = ({ input, step }: { input: ProblemInput; step: Step }) => {
const { A, b, c } = input
const rows = []
const canvasOptions: Parameters<typeof PrimaleCanvas>[0] = { A, b, c, B: step.B }
const A_B = A.slice({ rows: step.B })
const A_B_inverse = A_B.inverse2x2()
const b_B = b.slice(step.B)
rows.push(
<div class="row">
<Katex
formula={[
String.raw`A_B = ${matrixToLatex(A_B)}`,
String.raw`b_B = ${vectorToLatex(b_B)}`,
String.raw`c^t = ${rowVectorToLatex(c)}`,
].join(' \\qquad ')}
/>
</div>
)
const x = A_B_inverse.apply(b_B)
canvasOptions.x = x
rows.push(
<div class="row">
<Katex
formula={[
String.raw`\bar{x}`,
String.raw`A_B^{-1} b_B`,
String.raw`${matrixToLatex(A_B)}^{-1} ${vectorToLatex(b_B)}`,
String.raw`${matrixToLatex(A_B_inverse)} ${vectorToLatex(b_B)}`,
String.raw`${vectorToLatex(x)}`,
].join(' = ')}
/>
</div>
)
const y_B = A_B_inverse.transpose().apply(c)
rows.push(
<div class="row">
<Katex
formula={[
String.raw`\bar{y}_B^t`,
String.raw`c_B^t A_B^{-1}`,
String.raw`${rowVectorToLatex(c)} ${matrixToLatex(A_B_inverse)}`,
String.raw`${rowVectorToLatex(y_B)}`,
].join(' = ')}
/>
</div>
)
const y_Zero = Vector.zero(RationalField, A.rows)
const y = y_Zero.with(step.B, y_B)
rows.push(
<div class="row">
<Katex formula={String.raw`\implies \bar{y}^t = ${rowVectorToLatex(y)}`} />
</div>
)
const I_x = activeIndices(input, x)
rows.push(
<div class="row">
<Katex
formula={[
String.raw`I(\bar{x})`,
String.raw`\{ i \in \{1, \dots, m\} \mid A_i \bar{x}_i = b_i \}`,
indexSetToLatex(I_x),
].join(' = ')}
/>
</div>
)
const isDegenerate = I_x.length < A_B.rows
const isDualAdmissible = y_B.getData().every(y => y.geq(RationalField.zero))
const isDualDegenerate = y_B.getData().some(y => y.eq(RationalField.zero))
rows.push(
<div class="row">
<p>
La soluzione primale è <strong>{isDegenerate ? 'degenere' : 'non degenere'}</strong>.
</p>
<p>
La soluzione duale è <strong>{isDualAdmissible ? 'ammissibile' : 'non ammissibile'}</strong> e{' '}
<strong>{isDualDegenerate ? 'degenere' : 'non degenere'}</strong>.
</p>
</div>
)
if (!isDualAdmissible) {
const h = Math.min(...y.getData().flatMap((y, i) => (y.lt(RationalField.zero) ? [i] : [])))
rows.push(
<div class="row">
<Katex
formula={[
//
String.raw`h`,
String.raw`\min \{ i \in B \mid \bar{y}_i < 0 \}`,
String.raw`${h + 1}`,
].join(' = ')}
/>
</div>
)
const e_h = Vector.oneHot(RationalField, A.rows, h).slice(step.B)
console.log(e_h)
// const xi = Vec.neg(Mat.apply(A_B_inverse, e_h))
const xi = A_B_inverse.apply(e_h).neg()
rows.push(
<div class="row">
<Katex
formula={[
//
`\\xi`,
String.raw`-A_B^{-1} u_{B(h)}`,
String.raw`${vectorToLatex(xi)}`,
].join(' = ')}
/>
</div>
)
const N = range(0, A.rows).filter(i => !step.B.includes(i))
const A_N = A.slice({ rows: N })
const A_N__xi = A_N.apply(xi)
rows.push(
<div class="row">
<Katex
formula={[
[`N = \\{1, \\dots, m\\} \\setminus B = ${indexSetToLatex(N)}`],
[
`A_N \\xi`,
String.raw`${matrixToLatex(A_N)} ${vectorToLatex(xi)}`,
String.raw`${vectorToLatex(A_N__xi)}`,
].join(' = '),
].join(' \\qquad ')}
/>
</div>
)
if (!A_N__xi.getData().every(x => x.leq(RationalField.zero))) {
const positiveIndices = N.filter(i => A_N__xi.at(A_N.forwardRowIndices[i]).gt(RationalField.zero))
const [k, lambda] = positiveIndices
.map<[number, Rational]>(i => [i, b.at(i).sub(A.rowAt(i).dot(x)).div(A.rowAt(i).dot(xi))])
.reduce(([i1, lambda1], [i2, lambda2]) => (lambda2.lt(lambda1) ? [i2, lambda2] : [i1, lambda1]))
rows.push(
<div class="row">
<Katex
formula={[
`\\bar\\lambda`,
`\\min_i \\left\\{ \\frac{b_i - A_i \\bar{x}}{A_i \\xi} \\; \\middle| \\; i \\in N, A_i \\xi > 0 \\right\\}`,
`${lambda}`,
].join(' = ')}
/>
</div>
)
rows.push(
<div class="row">
<Katex
formula={[
`k`,
`\\argmin_i \\left\\{ \\bar\\lambda_i \\; \\middle| \\; i \\in N, A_i \\xi > 0 \\right\\}`,
`${k + 1}`,
].join(' = ')}
/>
</div>
)
rows.push(
<div class="row">
<Katex
formula={[
`\\implies B'`,
`B \\setminus \\{${h + 1}\\} \\cup \\{${k + 1}\\}`,
`${indexSetToLatex([...step.B.filter(i => i !== h), k].toSorted())}`,
].join(' = ')}
/>
</div>
)
} else {
rows.push(
<div class="row">
<p>
La soluzione duale è <strong>illimitata</strong>.
</p>
</div>
g.lineWidth = 30 / g.canvas.width
g.strokeStyle = 'darkgreen'
drawSimpleArrow(
g,
x1.toNumber(),
x2.toNumber(),
x1.toNumber() + c1.toNumber() / cLen,
x2.toNumber() + c2.toNumber() / cLen,
0.125
)
// draw xi
if (xi) {
const [xi1, xi2] = xi.getData()
const xiLen = Math.sqrt(xi1.toNumber() ** 2 + xi2.toNumber() ** 2)
g.strokeStyle = '#44d'
g.lineWidth = 50 / g.canvas.width
drawSimpleArrow(
g,
x1.toNumber(),
x2.toNumber(),
x1.toNumber() + xi1.toNumber() / xiLen,
x2.toNumber() + xi2.toNumber() / xiLen,
0.125
)
}
g.fillStyle = '#d44'
fillDot(g, x1.toNumber(), x2.toNumber(), 0.2)
}
}
return (
<div class="step">
<div class="algebraic-step">{rows}</div>
<div class="geometric-step">
<PrimaleCanvas {...canvasOptions} />
</div>
</div>
)
return <canvas ref={render} />
}
// export const PrimaleStep = ({ input, step }: { input: ProblemInput; step: Step }) => {
// const { A, b, c } = input
// const rows = []
// const canvasOptions: Parameters<typeof PrimalCanvas>[0] = { A, b, c, B: step.B }
// const A_B = A.slice({ rows: step.B })
// const A_B_inverse = A_B.inverse2x2()
// const b_B = b.slice(step.B)
// rows.push(
// <div class="row">
// <Katex
// formula={[
// String.raw`A_B = ${matrixToLatex(A_B)}`,
// String.raw`b_B = ${vectorToLatex(b_B)}`,
// String.raw`c^t = ${rowVectorToLatex(c)}`,
// ].join(' \\qquad ')}
// />
// </div>
// )
// const x = A_B_inverse.apply(b_B)
// canvasOptions.x = x
// rows.push(
// <div class="row">
// <Katex
// formula={[
// String.raw`\bar{x}`,
// String.raw`A_B^{-1} b_B`,
// String.raw`${matrixToLatex(A_B)}^{-1} ${vectorToLatex(b_B)}`,
// String.raw`${matrixToLatex(A_B_inverse)} ${vectorToLatex(b_B)}`,
// String.raw`${vectorToLatex(x)}`,
// ].join(' = ')}
// />
// </div>
// )
// const y_B = A_B_inverse.transpose().apply(c)
// rows.push(
// <div class="row">
// <Katex
// formula={[
// String.raw`\bar{y}_B^t`,
// String.raw`c_B^t A_B^{-1}`,
// String.raw`${rowVectorToLatex(c)} ${matrixToLatex(A_B_inverse)}`,
// String.raw`${rowVectorToLatex(y_B)}`,
// ].join(' = ')}
// />
// </div>
// )
// const y_Zero = Vector.zero(RationalField, A.rows)
// const y = y_Zero.with(step.B, y_B)
// rows.push(
// <div class="row">
// <Katex formula={String.raw`\implies \bar{y}^t = ${rowVectorToLatex(y)}`} />
// </div>
// )
// const I_x = activeIndices(input, x)
// rows.push(
// <div class="row">
// <Katex
// formula={[
// String.raw`I(\bar{x})`,
// String.raw`\{ i \in \{1, \dots, m\} \mid A_i \bar{x}_i = b_i \}`,
// indexSetToLatex(I_x),
// ].join(' = ')}
// />
// </div>
// )
// const isDegenerate = I_x.length < A_B.rows
// const isDualAdmissible = y_B.getData().every(y => y.geq(RationalField.zero))
// const isDualDegenerate = y_B.getData().some(y => y.eq(RationalField.zero))
// rows.push(
// <div class="row">
// <p>
// La soluzione primale è <strong>{isDegenerate ? 'degenere' : 'non degenere'}</strong>.
// </p>
// <p>
// La soluzione duale è <strong>{isDualAdmissible ? 'ammissibile' : 'non ammissibile'}</strong> e{' '}
// <strong>{isDualDegenerate ? 'degenere' : 'non degenere'}</strong>.
// </p>
// </div>
// )
// if (!isDualAdmissible) {
// const h = Math.min(...y.getData().flatMap((y, i) => (y.lt(RationalField.zero) ? [i] : [])))
// rows.push(
// <div class="row">
// <Katex
// formula={[
// //
// String.raw`h`,
// String.raw`\min \{ i \in B \mid \bar{y}_i < 0 \}`,
// String.raw`${h + 1}`,
// ].join(' = ')}
// />
// </div>
// )
// const e_h = Vector.oneHot(RationalField, A.rows, h).slice(step.B)
// console.log(e_h)
// // const xi = Vec.neg(Mat.apply(A_B_inverse, e_h))
// const xi = A_B_inverse.apply(e_h).neg()
// rows.push(
// <div class="row">
// <Katex
// formula={[
// //
// `\\xi`,
// String.raw`-A_B^{-1} u_{B(h)}`,
// String.raw`${vectorToLatex(xi)}`,
// ].join(' = ')}
// />
// </div>
// )
// const N = range(0, A.rows).filter(i => !step.B.includes(i))
// const A_N = A.slice({ rows: N })
// const A_N__xi = A_N.apply(xi)
// rows.push(
// <div class="row">
// <Katex
// formula={[
// [`N = \\{1, \\dots, m\\} \\setminus B = ${indexSetToLatex(N)}`],
// [
// `A_N \\xi`,
// String.raw`${matrixToLatex(A_N)} ${vectorToLatex(xi)}`,
// String.raw`${vectorToLatex(A_N__xi)}`,
// ].join(' = '),
// ].join(' \\qquad ')}
// />
// </div>
// )
// if (!A_N__xi.getData().every(x => x.leq(RationalField.zero))) {
// const positiveIndices = N.filter(i => A_N__xi.at(A_N.forwardRowIndices[i]).gt(RationalField.zero))
// const [k, lambda] = positiveIndices
// .map<[number, Rational]>(i => [i, b.at(i).sub(A.rowAt(i).dot(x)).div(A.rowAt(i).dot(xi))])
// .reduce(([i1, lambda1], [i2, lambda2]) => (lambda2.lt(lambda1) ? [i2, lambda2] : [i1, lambda1]))
// rows.push(
// <div class="row">
// <Katex
// formula={[
// `\\bar\\lambda`,
// `\\min_i \\left\\{ \\frac{b_i - A_i \\bar{x}}{A_i \\xi} \\; \\middle| \\; i \\in N, A_i \\xi > 0 \\right\\}`,
// `${lambda}`,
// ].join(' = ')}
// />
// </div>
// )
// rows.push(
// <div class="row">
// <Katex
// formula={[
// `k`,
// `\\argmin_i \\left\\{ \\bar\\lambda_i \\; \\middle| \\; i \\in N, A_i \\xi > 0 \\right\\}`,
// `${k + 1}`,
// ].join(' = ')}
// />
// </div>
// )
// rows.push(
// <div class="row">
// <Katex
// formula={[
// `\\implies B'`,
// `B \\setminus \\{${h + 1}\\} \\cup \\{${k + 1}\\}`,
// `${indexSetToLatex([...step.B.filter(i => i !== h), k].toSorted())}`,
// ].join(' = ')}
// />
// </div>
// )
// } else {
// rows.push(
// <div class="row">
// <p>
// La soluzione duale è <strong>illimitata</strong>.
// </p>
// </div>
// )
// }
// }
// return (
// <div class="step">
// <div class="algebraic-step">{rows}</div>
// <div class="geometric-step">
// <PrimalCanvas {...canvasOptions} />
// </div>
// </div>
// )
// }

@ -30,7 +30,7 @@ export function drawSemiplane(
p2 = b / a2
}
const normalize = Math.sqrt(a1 ** 2 + a2 ** 2) * 1.5
const normalize = Math.sqrt(a1 ** 2 + a2 ** 2) * 1.25
const gradient = g.createLinearGradient(p1, p2, p1 - a1 / normalize, p2 - a2 / normalize)
gradient.addColorStop(0, gradientAccent)
@ -81,3 +81,40 @@ export function drawSemiplane(
}
g.stroke()
}
export function drawSimpleArrow(
g: CanvasRenderingContext2D,
x1: number,
y1: number,
x2: number,
y2: number,
size: number
) {
const arrowLength = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
g.save()
g.beginPath()
g.translate(x1, y1)
g.rotate(Math.atan2(y2 - y1, x2 - x1))
g.moveTo(0, 0)
g.lineTo(arrowLength, 0)
g.moveTo(arrowLength - size, -size)
g.lineTo(arrowLength, 0)
g.moveTo(arrowLength - size, +size)
g.lineTo(arrowLength, 0)
g.stroke()
g.restore()
}
export function fillDot(g: CanvasRenderingContext2D, x: number, y: number, radius: number) {
g.beginPath()
g.arc(x, y, radius, 0, 2 * Math.PI)
g.fill()
}
export function strokeDot(g: CanvasRenderingContext2D, x: number, y: number, radius: number) {
g.beginPath()
g.arc(x, y, radius, 0, 2 * Math.PI)
g.stroke()
}

@ -0,0 +1,241 @@
import { indexSetToLatex, matrixToLatex, rowVectorToLatex, vectorToLatex } from '../latex'
import { range } from '../math'
import { Matrix } from '../matrix'
import { Rational, RationalField } from '../rationals'
import { Vector } from '../vector'
export type ProblemInput = {
A: Matrix<Rational>
b: Vector<Rational>
c: Vector<Rational>
B: number[]
maxIterations?: number
}
export type ProblemComment = { type: 'formula' | 'text'; content: string }
export type ProblemStep = {
B: number[]
x?: Vector<Rational>
xi?: Vector<Rational>
comments: ProblemComment[]
}
export type ProblemStatus =
| { result: 'optimal'; x: Vector<Rational> }
| { result: 'unbounded'; xi: Vector<Rational> }
| { result: 'infeasible' }
| { result: 'too-many-iterations' }
export type ProblemOutput = {
status: ProblemStatus
steps: ProblemStep[]
}
const activeIndices = (input: ProblemInput, x: Vector<Rational>): number[] => {
const { A, b } = input
const A_x = A.apply(x)
return A_x.getData().flatMap((a, i) => (a.eq(b.at(i)) ? [i] : []))
}
export function computePrimalSimplexSteps(input: ProblemInput): ProblemOutput {
input.maxIterations ??= 10
const { A, b, c } = input
let B = input.B
let stepResult_B: number[] | undefined = undefined
let stepResult_xi: Vector<Rational> | undefined = undefined
let status: ProblemStatus | null = null
const steps: ProblemStep[] = []
while (status === null && steps.length < input.maxIterations) {
const comments: ProblemComment[] = []
const A_B = A.slice({ rows: B })
const A_B_inverse = A_B.inverse2x2()
const b_B = b.slice(B)
comments.push({
type: 'formula',
content: [
`A_B = ${matrixToLatex(A_B)}`,
`b_B = ${vectorToLatex(b_B)}`,
`c^t = ${rowVectorToLatex(c)}`,
].join(' \\qquad '),
})
const x = A_B_inverse.apply(b_B)
comments.push({
type: 'formula',
content: [
String.raw`\bar{x}`,
`A_B^{-1} b_B`,
`${matrixToLatex(A_B)}^{-1} ${vectorToLatex(b_B)}`,
`${matrixToLatex(A_B_inverse)} ${vectorToLatex(b_B)}`,
`${vectorToLatex(x)}`,
].join(' = '),
})
const y_B = A_B_inverse.transpose().apply(c)
comments.push({
type: 'formula',
content: [
String.raw`\bar{y}_B^t`,
`c_B^t A_B^{-1}`,
`${rowVectorToLatex(c)} ${matrixToLatex(A_B_inverse)}`,
`${rowVectorToLatex(y_B)}`,
].join(' = '),
})
const y_Zero = Vector.zero(RationalField, A.rows)
const y = y_Zero.with(B, y_B)
comments.push({ type: 'formula', content: String.raw`\implies \bar{y}^t = ${rowVectorToLatex(y)}` })
const I_x = activeIndices(input, x)
comments.push({
type: 'formula',
content: [
String.raw`I(\bar{x})`,
String.raw`\{ i \in \{1, \dots, m\} \mid A_i \bar{x}_i = b_i \}`,
indexSetToLatex(I_x),
].join(' = '),
})
const isDegenerate = I_x.length < A_B.rows
const isDualAdmissible = y_B.getData().every(y => y.geq(RationalField.zero))
const isDualDegenerate = y_B.getData().some(y => y.eq(RationalField.zero))
comments.push({
type: 'text',
content: `La soluzione primale è *${isDegenerate ? 'degenere' : 'non degenere'}*.`,
})
comments.push({
type: 'text',
content: `La soluzione duale è *${isDualAdmissible ? 'ammissibile' : 'non ammissibile'}* e *${
isDualDegenerate ? 'degenere' : 'non degenere'
}*.`,
})
if (!isDualAdmissible) {
const h = Math.min(...y.getData().flatMap((y, i) => (y.lt(RationalField.zero) ? [i] : [])))
comments.push({
type: 'formula',
content: [
// prettier-ignore
`h`,
String.raw`\min \{ i \in B \mid \bar{y}_i < 0 \}`,
`${h + 1}`,
].join(' = '),
})
const e_h = Vector.oneHot(RationalField, A.rows, h).slice(B)
console.log(e_h)
const xi = A_B_inverse.apply(e_h).neg()
stepResult_xi = xi
comments.push({
type: 'formula',
content: [
// prettier-ignore
`\\xi`,
`-A_B^{-1} u_{B(h)}`,
`${vectorToLatex(xi)}`,
].join(' = '),
})
const N = range(0, A.rows).filter(i => !B.includes(i))
const A_N = A.slice({ rows: N })
const A_N__xi = A_N.apply(xi)
comments.push({
type: 'formula',
content: [
[`N = \\{1, \\dots, m\\} \\setminus B = ${indexSetToLatex(N)}`],
[`A_N \\xi`, `${matrixToLatex(A_N)} ${vectorToLatex(xi)}`, `${vectorToLatex(A_N__xi)}`].join(' = '),
].join(' \\qquad '),
})
if (!A_N__xi.getData().every(x => x.leq(RationalField.zero))) {
const [k, lambda] = N.filter(i => A_N__xi.at(A_N.forwardRowIndices[i]).gt(RationalField.zero))
.map<[number, Rational]>(i => [i, b.at(i).sub(A.rowAt(i).dot(x)).div(A.rowAt(i).dot(xi))])
.reduce(([i1, lambda1], [i2, lambda2]) => (lambda1.lt(lambda2) ? [i1, lambda1] : [i2, lambda2]))
comments.push({
type: 'formula',
content: [
`\\bar\\lambda`,
`\\min_i \\left\\{ \\frac{b_i - A_i \\bar{x}}{A_i \\xi} \\; \\middle| \\; i \\in N, A_i \\xi > 0 \\right\\}`,
`${lambda}`,
].join(' = '),
})
comments.push({
type: 'formula',
content: [
`k`,
`\\argmin_i \\left\\{ \\bar\\lambda_i \\; \\middle| \\; i \\in N, A_i \\xi > 0 \\right\\}`,
`${k + 1}`,
].join(' = '),
})
comments.push({
type: 'formula',
content: [
`B'`,
`B \\setminus \\{h + 1\\} \\cup \\{k + 1\\}`,
`${indexSetToLatex([...B.filter(i => i !== h), k].toSorted())}`,
].join(' = '),
})
stepResult_B = [...B.filter(i => i !== h), k].toSorted()
} else {
comments.push({ type: 'text', content: 'La soluzione è *illimitata*' })
status = { result: 'unbounded', xi }
}
} else {
comments.push({ type: 'text', content: 'La soluzione è *ottima*' })
status = { result: 'optimal', x }
}
steps.push({
B,
x,
xi: stepResult_xi,
comments,
})
if (stepResult_B) {
B = stepResult_B
stepResult_B = undefined
stepResult_xi = undefined
}
}
if (status === null) {
status = { result: 'too-many-iterations' }
}
return {
status,
steps,
}
}

@ -54,11 +54,6 @@ const App = () => {
<h2>Svolgimento</h2>
{'result' in problemValuesResult && <Primale input={problemValuesResult.result} />}
<h2>Debug</h2>
<pre>
<code>{JSON.stringify(problemValuesResult, null, 4)}</code>
</pre>
</>
)
}

@ -9,9 +9,12 @@
box-sizing: border-box;
}
html {
height: 100%;
}
html,
body {
height: 100%;
min-height: 100%;
}
@ -45,6 +48,10 @@
color: #333;
}
p {
line-height: 1.75;
}
pre,
code {
font-family: 'JetBrains Mono', monospace;
@ -66,7 +73,7 @@
color: #333;
padding: 3rem 1rem;
padding: 6rem 1rem;
> * {
display: block;
@ -91,6 +98,7 @@
display: grid;
grid-auto-flow: row;
justify-items: center;
grid-template-columns: 1fr 1fr;
border: 1px solid #ddd;
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.1);
@ -101,10 +109,13 @@
max-width: fit-content;
> .step {
grid-column: span 2;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-columns: subgrid;
justify-items: center;
align-items: center;
/* align-items: center; */
gap: 0;
@media (width < 768px) {
@ -112,17 +123,29 @@
}
> .algebraic-step {
justify-self: stretch;
/* border: 1px solid #ddd; */
/* box-shadow: 0 0 1rem rgba(0, 0, 0, 0.1); */
border-radius: 1rem;
padding: 0 1rem;
> * + * {
margin-top: 0.5rem;
}
}
> .geometric-step {
/* padding: 1rem; */
min-width: 30rem;
}
padding: 2rem 0;
&:not(:first-child) {
border-top: 1px solid #ddd;
}
}
}
}

Loading…
Cancel
Save