package org.openlca.eigen.solvers;
import org.openlca.core.math.IMatrix;
import org.openlca.eigen.Numbers;
public class SeqAgg {
private final IMatrix a;
private final IMatrix b;
private final int refIdx;
private final double scale;
public SeqAgg(IMatrix a, IMatrix b, int refIdx, double demand) {
this.a = a.copy();
this.b = b.copy();
this.refIdx = refIdx;
this.scale = demand / a.get(refIdx, refIdx);
}
public double[] solve() {
for (int sourceIdx = 0; sourceIdx < a.columns(); sourceIdx++) {
if (sourceIdx == refIdx)
continue;
if (refIdx < sourceIdx)
add(sourceIdx, refIdx);
for (int targetIdx = sourceIdx + 1; targetIdx < a
.columns(); targetIdx++) {
add(sourceIdx, targetIdx);
}
}
double[] refCol = b.getColumn(refIdx);
for (int i = 0; i < refCol.length; i++)
refCol[i] *= scale;
return refCol;
}
/** Adds the source column to the target column. */
private void add(int sourceIdx, int targetIdx) {
double ts = a.get(sourceIdx, targetIdx);
double st = a.get(targetIdx, sourceIdx);
if (Numbers.isZero(ts) && Numbers.isZero(st))
return; // independent columns
double ss = a.get(sourceIdx, sourceIdx);
double tt = a.get(targetIdx, targetIdx);
if (Numbers.isZero(ss) || Numbers.isZero(tt))
return; // TODO: log warning: no diagonal entry
if (Numbers.isZero(st) && !Numbers.isZero(ts)) {
// linear adding
double factor = -ts / ss;
add(sourceIdx, targetIdx, factor);
} else if (!Numbers.isZero(st) && !Numbers.isZero(ts)) {
// loop adding
double q = (ts / ss) * (st / tt);
// if (q >= 1 || q < 0)
// throw new RuntimeException("Non-convergent loop");
double loopFactor = 1 / (1 - q);
scaleColumn(targetIdx, loopFactor);
double factor = -ts / ss;
add(sourceIdx, targetIdx, loopFactor * factor);
}
}
private void add(int sourceIdx, final int targetIdx, final double factor) {
for (int row = 0; row < a.rows(); row++) {
double val = a.get(row, sourceIdx);
double addVal = val * factor;
double oldEntry = a.get(row, targetIdx);
a.set(row, targetIdx, oldEntry + addVal);
}
for (int row = 0; row < b.rows(); row++) {
double val = b.get(row, sourceIdx);
double addVal = val * factor;
double oldEntry = b.get(row, targetIdx);
b.set(row, targetIdx, oldEntry + addVal);
}
}
private void scaleColumn(final int columnIndex, final double factor) {
for (int row = 0; row < a.rows(); row++) {
double val = a.get(row, columnIndex);
if (Numbers.isZero(val))
continue;
a.set(row, columnIndex, val * factor);
}
for (int row = 0; row < b.rows(); row++) {
double val = b.get(row, columnIndex);
if (Numbers.isZero(val))
continue;
b.set(row, columnIndex, val * factor);
}
}
}