Skip to content

Commit d78ac0f

Browse files
committed
AtomMatcher charge/isotope matching, GameTheory short-circuit, matrix optimization
Chemical correctness: - AtomMatcher: Add charge matching to all 4 matchers — C+ no longer matches C, NH4+ no longer matches NH3. Prevents incorrect mapping of charged species in ionic/zwitterionic reactions - AtomMatcher: Add isotope matching (when both atoms have explicit mass numbers) — deuterium no longer matches protium - AtomMatcher: Refactor 4 duplicated helpers (atomicNumber, matchAtomType, isRingSizeMatch) into shared parent methods. Fix class naming convention Performance: - GameTheory{Rings,Max,Min,Mixture}: Add early termination when graph similarity matrix is all-zero (no remaining mappable pairs). Skips unnecessary UpdateMatrix() calls on fully-mapped reactions - EBIMatrix.times(): Cache-friendly i,k,j loop order for matrix multiplication with zero-skip optimization - EBIMatrix.getValueUnsafe(): Bounds-check-free accessor for hot paths All 135 tests pass. Author: Syed Asad Rahman <asad.rahman@bioinceptionlabs.com>
1 parent d6d29ad commit d78ac0f

6 files changed

Lines changed: 220 additions & 169 deletions

File tree

src/main/java/com/bioinceptionlabs/reactionblast/mapping/algorithm/GameTheoryMax.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,23 @@ private void GenerateMapping(boolean flag) throws Exception {
173173
if (winner.getFlag()) {
174174
LOGGER.debug("**********Updated Mapping**************");
175175
UpdateMapping();
176+
177+
// Early termination if no remaining mappable pairs
178+
boolean hasRemainingPairs = false;
179+
for (int i = 0; i < mh.getGraphSimilarityMatrix().getRowDimension(); i++) {
180+
for (int j = 0; j < mh.getGraphSimilarityMatrix().getColumnDimension(); j++) {
181+
if (mh.getGraphSimilarityMatrix().getValue(i, j) > 0) {
182+
hasRemainingPairs = true;
183+
break;
184+
}
185+
}
186+
if (hasRemainingPairs) break;
187+
}
188+
if (!hasRemainingPairs) {
189+
LOGGER.debug("Early termination: no remaining mappable pairs");
190+
break;
191+
}
192+
176193
LOGGER.debug("**********Updated Matrix**************");
177194
UpdateMatrix(mh, removeHydrogen);
178195
LOGGER.debug("**********Generate Mapping**************");

src/main/java/com/bioinceptionlabs/reactionblast/mapping/algorithm/GameTheoryMin.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,23 @@ private void GenerateMapping(boolean flag) throws Exception {
201201
if (winner.getFlag()) {
202202
LOGGER.debug("**********Updated Mapping**************");
203203
UpdateMapping();
204+
205+
// Early termination if no remaining mappable pairs
206+
boolean hasRemainingPairs = false;
207+
for (int i = 0; i < mh.getGraphSimilarityMatrix().getRowDimension(); i++) {
208+
for (int j = 0; j < mh.getGraphSimilarityMatrix().getColumnDimension(); j++) {
209+
if (mh.getGraphSimilarityMatrix().getValue(i, j) > 0) {
210+
hasRemainingPairs = true;
211+
break;
212+
}
213+
}
214+
if (hasRemainingPairs) break;
215+
}
216+
if (!hasRemainingPairs) {
217+
LOGGER.debug("Early termination: no remaining mappable pairs");
218+
break;
219+
}
220+
204221
LOGGER.debug("**********Updated Matrix**************");
205222
UpdateMatrix(mh, removeHydrogen);
206223
LOGGER.debug("**********Generate Mapping**************");

src/main/java/com/bioinceptionlabs/reactionblast/mapping/algorithm/GameTheoryMixture.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,23 @@ private void GenerateMapping(boolean flag) throws Exception {
181181

182182
LOGGER.debug("**********Updated Mapping**************");
183183
UpdateMapping();
184+
185+
// Early termination if no remaining mappable pairs
186+
boolean hasRemainingPairs = false;
187+
for (int i = 0; i < mh.getGraphSimilarityMatrix().getRowDimension(); i++) {
188+
for (int j = 0; j < mh.getGraphSimilarityMatrix().getColumnDimension(); j++) {
189+
if (mh.getGraphSimilarityMatrix().getValue(i, j) > 0) {
190+
hasRemainingPairs = true;
191+
break;
192+
}
193+
}
194+
if (hasRemainingPairs) break;
195+
}
196+
if (!hasRemainingPairs) {
197+
LOGGER.debug("Early termination: no remaining mappable pairs");
198+
break;
199+
}
200+
184201
LOGGER.debug("**********Updated Matrix**************");
185202
UpdateMatrix(mh, removeHydrogen);
186203
LOGGER.debug("**********Generate Mapping**************");

src/main/java/com/bioinceptionlabs/reactionblast/mapping/algorithm/GameTheoryRings.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,23 @@ private void GenerateMapping() throws Exception {
190190

191191
LOGGER.debug("**********Updated Mapping**************");
192192
UpdateMapping();
193+
194+
// Early termination if no remaining mappable pairs
195+
boolean hasRemainingPairs = false;
196+
for (int i = 0; i < mh.getGraphSimilarityMatrix().getRowDimension(); i++) {
197+
for (int j = 0; j < mh.getGraphSimilarityMatrix().getColumnDimension(); j++) {
198+
if (mh.getGraphSimilarityMatrix().getValue(i, j) > 0) {
199+
hasRemainingPairs = true;
200+
break;
201+
}
202+
}
203+
if (hasRemainingPairs) break;
204+
}
205+
if (!hasRemainingPairs) {
206+
LOGGER.debug("Early termination: no remaining mappable pairs");
207+
break;
208+
}
209+
193210
LOGGER.debug("**********Updated Matrix**************");
194211
UpdateMatrix(mh, removeHydrogen);
195212
LOGGER.debug("**********Generate Mapping**************");

src/main/java/com/bioinceptionlabs/reactionblast/tools/EBIMatrix.java

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,19 @@ public double getValue(int i, int j) {
433433
return val;
434434
}
435435

436+
/**
437+
* Get a single element without bounds checking.
438+
* Use this on hot paths where indices are already known to be valid,
439+
* to avoid the overhead of bounds checking and logging in {@link #getValue}.
440+
*
441+
* @param i Row index.
442+
* @param j Column index.
443+
* @return matrix(i,j)
444+
*/
445+
public double getValueUnsafe(int i, int j) {
446+
return matrix[i][j];
447+
}
448+
436449
/**
437450
* Make a deep duplicate of a matrix
438451
*
@@ -1243,20 +1256,24 @@ public EBIMatrix times(EBIMatrix B) {
12431256
if (B.getRowDimension() != columns) {
12441257
throw new IllegalArgumentException("EBIMatrix inner dimensions must agree.");
12451258
}
1246-
EBIMatrix X = new EBIMatrix(rows, B.getColumnDimension());
1259+
int bCols = B.getColumnDimension();
1260+
EBIMatrix X = new EBIMatrix(rows, bCols);
12471261
double[][] C = X.getArray();
1248-
double[] Bcolj = new double[columns];
1249-
for (int j = 0; j < B.getColumnDimension(); j++) {
1262+
double[][] Barray = B.getArray();
1263+
// Cache-friendly i,k,j loop order: iterate over rows of A, then
1264+
// for each element A[i][k], scatter-multiply across B's row k.
1265+
// This accesses both A and B in row-major order, maximizing L1/L2
1266+
// cache utilization and avoiding the column-copy temporary array.
1267+
for (int i = 0; i < rows; i++) {
1268+
double[] Arowi = matrix[i];
1269+
double[] Crowi = C[i];
12501270
for (int k = 0; k < columns; k++) {
1251-
Bcolj[k] = B.matrix[k][j];
1252-
}
1253-
for (int i = 0; i < rows; i++) {
1254-
double[] Arowi = matrix[i];
1255-
double s = 0;
1256-
for (int k = 0; k < columns; k++) {
1257-
s += Arowi[k] * Bcolj[k];
1271+
double aik = Arowi[k];
1272+
if (aik == 0.0) continue;
1273+
double[] Browk = Barray[k];
1274+
for (int j = 0; j < bCols; j++) {
1275+
Crowi[j] += aik * Browk[j];
12581276
}
1259-
C[i][j] = s;
12601277
}
12611278
}
12621279
return X;

0 commit comments

Comments
 (0)