Skip to content

fix(trade): keep dimnames on the all-zero .balance_matrix() path - #765

Open
lbm364dl wants to merge 1 commit into
mainfrom
claude/bilateral-trade-unnamed-matrix-235
Open

fix(trade): keep dimnames on the all-zero .balance_matrix() path#765
lbm364dl wants to merge 1 commit into
mainfrom
claude/bilateral-trade-unnamed-matrix-235

Conversation

@lbm364dl

Copy link
Copy Markdown
Collaborator

What was wrong

.balance_matrix() (R/bilateral_trade.R) short-circuits when the balanced
export and import targets are all zero:

if (sum(exports) == 0 && sum(imports) == 0) {
  return(matrix(0, nrow = n, ncol = n))
}

That return carries no dimnames, while the normal return path builds its result
with dimnames = dimnames(trade_matrix). The documented contract for these
matrices (R/bilateral_trade.R:26) is name indexing — "the value at
m["A", "B"] is the trade in tonnes" — so the zero path quietly violated it.
It is benign today only because both consumers short-circuit on zero content
before indexing, which is exactly the accident worth pinning.

Classification: mechanical — a contract/crash fix with no defensible
alternative and no numeric effect (the returned values are all zero either
way).

What changed

  • The zero path now sets dimnames = dimnames(trade_matrix), the same way the
    normal path does.
  • New test in tests/testthat/test_bilateral_trade.R:
    .balance_matrix keeps dimnames when all trade is zero — feeds an all-zero
    group with named dimensions through and asserts both dimnames(result) and
    result["1", "2"].

The pre-existing .balance_matrix returns zero matrix when all trade is zero
test passes an unnamed input matrix, so it keeps passing unchanged.

Verification

Failing before the fix (source change stashed, new test present):

── Failed tests ────────────────────────────────────────────────────────────────
Failure ('test_bilateral_trade.R:669:3'): .balance_matrix keeps dimnames when all trade is zero
dimnames(result) (`actual`) not equal to list(codes, codes) (`expected`).

`actual` is NULL
`expected` is a list

Error ('test_bilateral_trade.R:670:3'): .balance_matrix keeps dimnames when all trade is zero
Error in `result["1", "2"]`: no 'dimnames' attribute for array

[ FAIL 2 | WARN 0 | SKIP 0 | PASS 87 ]

Passing after the fix:

✔ |         89 | bilateral_trade
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 89 ]

Also ran air format . (no further changes to other files) and lintr::lint()
on both changed files — no lints. No roxygen changed, so no document() run
was needed; no NEWS.md entry, since no published value changes.

Closes #235.

🤖 Generated with Claude Code

.balance_matrix() short-circuits when balanced exports and imports are all
zero, and returned matrix(0, nrow = n, ncol = n) with no dimnames, while the
normal return path copies dimnames(trade_matrix). The documented contract for
the bilateral trade matrices is name indexing (m["A", "B"]), so the zero path
quietly broke it; it was benign only because both consumers short-circuit on
zero content before indexing.

Set dimnames on the zero path the same way the normal path does, and pin the
contract with a test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lbm364dl lbm364dl self-assigned this Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bilateral_trade: all-zero trade group returns an unnamed matrix, violating the documented name-indexing contract

1 participant