soma.cell

Coin change, minimum number of coins to make an amount (unbounded knapsack)

This is the "fewest coins" variant — distinct from counting the number of ways. Demonstrates: a 1D DP seeded with a big sentinel, the min-over-coins recurrence, and returning -1 when an amount is unreachable.

A Soma program from the algorithms part of the verified corpus: 49 lines, cells MinCoins, MinCoinsTests. It passes soma check with soma 3.2.4.

How it is built

The cell MinCoins declares 2 signals in its face: fewest(amount: Int) -> Int and solve(coins: List, amount: Int) -> Int. Handlers: fewest (line 15) and solve (line 19).

The file also holds 1 test cell (MinCoinsTests) with 7 assertions, run by soma test.

Generated from soma describe on this file.

The source

min_coins.cell

// Coin change, minimum number of coins to make an amount (unbounded knapsack).
// This is the "fewest coins" variant — distinct from counting the number of
// ways. Demonstrates: a 1D DP seeded with a big sentinel, the min-over-coins
// recurrence, and returning -1 when an amount is unreachable.
//
//   soma run  examples/corpus/algorithms/min_coins.cell fewest 11
//   soma test examples/corpus/algorithms/min_coins.cell

cell MinCoins {
    face {
        signal fewest(amount: Int) -> Int
        signal solve(coins: List, amount: Int) -> Int
    }

    on fewest(amount: Int) {
        return solve(list(1, 2, 5), amount)
    }

    on solve(coins: List, amount: Int) {
        let inf = 1000000
        // dp[a] = fewest coins to make a, or inf if impossible
        let dp = list()
        for a in range(0, amount + 1) { dp = push(dp, inf) }
        dp[0] = 0
        for a in range(1, amount + 1) {
            for c in coins {
                if c <= a {
                    let cand = dp[a - c] + 1
                    if cand < dp[a] { dp[a] = cand }
                }
            }
        }
        if dp[amount] >= inf { return -1 }
        return dp[amount]
    }
}

cell test MinCoinsTests {
    rules {
        assert fewest(11) == 3        // 5 + 5 + 1
        assert fewest(0) == 0
        assert fewest(3) == 2         // 2 + 1
        assert fewest(25) == 5        // 5*5
        assert solve(list(2), 3) == -1
        assert solve(list(2), 4) == 2
        assert solve(list(1, 5, 10, 25), 30) == 2
    }
}

What the toolchain says

Run on a fresh copy of the program at build time; the outputs are pasted unedited.

$ soma check min_coins.cell
✓ All checks passed.
$ soma test min_coins.cell
test MinCoinsTests ...
  ✓ assert fewest(11) == 3
  ✓ assert fewest(0) == 0
  ✓ assert fewest(3) == 2
  ✓ assert fewest(25) == 5
  ✓ assert solve(list(2), 3) == -1
  ✓ assert solve(list(2), 4) == 2
  ✓ assert solve(list(1, 5, 10, 25), 30) == 2

7 tests: 7 passed, 0 failed
$ soma verify min_coins.cell
Verifying min_coins.cell...
No state machine in this program: nothing to prove beyond `soma check` (invariants and lifecycles are what verify proves).
VERIFY OK (vacuous — no state machine)
$ soma run min_coins.cell fewest 11
3

Builtins used

list, push, range

Source file: /corpus/algorithms/min_coins.cell · examples/corpus/algorithms/min_coins.cell