Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/modules/staffing/optimizer.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | internal/modules/staffing/optimizer.go |
| Original SHA-256 | eadd61d5090ddb497252f4298d57e399262fe6cc4035e7c691e17fcea6121018 |
| Git blob SHA | d1133850abb984cc059592597f2804a45098e67d |
| Reading-copy SHA-256 | eadd61d5090ddb497252f4298d57e399262fe6cc4035e7c691e17fcea6121018 |
Every recorded occurrence
These 24 records contain identical original source bytes. Their paths and revisions remain part of the archive.
| Original record | Branch or archive | Revision / identifier | Original location |
|---|---|---|---|
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | feature/opportunity-to-outcome | 720d47fae476b0d38477ae4401697d372b1ea2bc | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | per-tenant-routing | 9bde0f9a74b89d01980fa658e5253acda7528d0c | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | collision-deconfliction | 4ee5a09972608ed89f8377c6665542d5f450f0a6 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | ws6-retrieval | 6c56e29231ec05735927fa265c97d517499dfeaf | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | m5-price-to-win | 41000ad30fff4129fdaf5ec84e15ba14c27d4890 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | ws1-ontology-sync | b596f449a32a1016fbd403c36505cc00a1ac9ee6 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | master | 1e9249840a4302ec82e123b9c9cb6f75badea3e4 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | ws8-teaching-loop | c823f0fb6bb2aba0f39001eb6de96f4000d9321f | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | w3-ai-staffing-recommendations | 43d56387270ef4da4cabd794d97ca6a49909bc7d | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | mag-substrate | b82c7100a492dec67dfd1eda6b3f53def859be35 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | ws3-clearance | 4b8e7ce594c068621867f9c9deed0ec64ae6c553 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | phase2-eval-fairness | d361af254701975fe239927656ababb56374655e | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | time-to-fill-survival | b66e1e505863d4f17741a4b9ede73509e45bd6c6 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | graphrag-foundation | b0ddeb9f633fd138d09309d1a17b7d13bc3a67f7 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | deploy-and-ops | bdacd9d0b4d5998d25fc74bf4007f4c3ec538c7a | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | ws9-bench-ingestion | f585a9ef32fa554663ce1fba9a414a54b6e49cf0 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | ws2-alias-relationship | e4dada4601c6eb101830f9d223a9655dfa120616 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | presentation-readiness | eb574acaa29d71ce4d0f852572e87c275c79fdb7 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | vendor-intelligence | fe9796a19c261951d3f2565206c0c1b5ac294465 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | ws7-federal-signal | 6a9f6ce9cfb13345ffb47770d4960e7c2d9c17c2 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | ai-brain-live | aac32fc49c878cc5bca099459307611562d0bac1 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | phase2-connectors | e038d4c687aec16f8bd9bc4d907eea14d91f8704 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | feature/aioe-upgrade | 2178e7298d0e5d9af83afa12402fd587f0ba6ed8 | Open original |
| Janady13/missionmatch / internal/modules/staffing/optimizer.go | capture-readiness | bae6af7cb2997e819174a9e9cfff5e16067a1975 | Open original |
Recovered source transcript
Download complete source text ↓343 lines · 11.7 KB
package staffing
import (
"container/heap"
"context"
"errors"
"sort"
)
// The portfolio view allocates the bench across all open requirements at once to maximize total
// assignment value. This is a MAXIMUM PROFIT UNBALANCED ASSIGNMENT, solved exactly as a minimum cost flow
// by successive shortest paths with node potentials. The math is the classic assignment-as-min-cost-flow
// (Tomizawa 1971; Edmonds and Karp 1972; Ahuja, Magnanti, Orlin, Network Flows, ch 9 and 12):
//
// - Build source -> candidate (cap 1, cost 0), candidate -> requirement (cap 1, cost = negative scaled
// value, only for qualified pairs), requirement -> sink (cap = open slots, cost 0).
// - Seed node potentials with one Bellman Ford pass, because maximizing makes the cost edges negative.
// - Each iteration, find the minimum reduced cost augmenting path with Dijkstra over non negative
// reduced costs, push one unit, and update potentials by the Dijkstra distances. Potentials keep all
// residual reduced costs non negative, so Dijkstra stays valid for the life of the solve.
// - STOP as soon as the cheapest augmenting path has non negative real cost: any further augmentation
// could not increase total profit. The cost vs flow curve is convex, so this is the exact optimum.
//
// It is integer exact (unit capacities, integral supplies, total unimodularity, all int64 arithmetic, no
// float in the objective) and deterministic (stable node ids from the input order, a fixed adjacency
// order, and a priority queue that breaks ties by node id). Candidates and slots may be left unmatched,
// which is exactly a SOURCE recommendation in the domain. The optimizer never qualifies or disqualifies a
// candidate: the caller adds an edge only where the deterministic engine already qualified the pair.
var (
// ErrProblemTooLarge is returned when the candidate by requirement graph exceeds the configured size
// cap. An unbounded solve is a denial of service on our own API, so a too large problem is refused with
// a clear error rather than run.
ErrProblemTooLarge = errors.New("staffing: portfolio problem exceeds the configured size cap")
// ErrSolveBudget is returned when the solve exceeds its wall clock budget (the context deadline). The
// solve never hangs.
ErrSolveBudget = errors.New("staffing: portfolio solve exceeded the time budget")
)
// AssignEdge is a qualified candidate to requirement pairing with its scaled objective value (> 0).
type AssignEdge struct {
CandidateIdx int
RequirementIdx int
Value int64
}
// PortfolioInput is the assignment problem: how many candidates, how many open slots per requirement, and
// the qualified, valued edges. Candidate and requirement indices are 0 based and must be stable.
type PortfolioInput struct {
NumCandidates int
Slots []int // open slots per requirement; len is the requirement count
Edges []AssignEdge
}
// Assignment is one candidate placed on one requirement in the optimal allocation.
type Assignment struct {
CandidateIdx int
RequirementIdx int
Value int64
}
// PortfolioSolution is the optimal max profit allocation. Unassigned candidates and unfilled slots are
// implicit (anything not in Assignments).
type PortfolioSolution struct {
Assignments []Assignment
TotalValue int64
}
// SolvePortfolio computes the maximum profit allocation. maxEdges and maxNodes bound the problem; the
// context deadline bounds the wall clock. An empty problem (no candidates, requirements, or edges) returns
// an empty allocation with no error, not a failure.
func SolvePortfolio(ctx context.Context, in PortfolioInput, maxEdges, maxNodes int) (PortfolioSolution, error) {
R := len(in.Slots)
C := in.NumCandidates
n := C + R + 2
if len(in.Edges) > maxEdges || n > maxNodes {
return PortfolioSolution{}, ErrProblemTooLarge
}
if C == 0 || R == 0 || len(in.Edges) == 0 {
return PortfolioSolution{}, nil
}
const source = 0
candNode := func(i int) int { return 1 + i }
reqNode := func(j int) int { return 1 + C + j }
sink := n - 1
g := newMCMF(n)
for i := 0; i < C; i++ {
g.addEdge(source, candNode(i), 1, 0)
}
// Add candidate to requirement edges in a fixed order so the residual graph is built identically every
// run (Go map iteration is randomized; the caller's slice order is the source of truth).
edges := append([]AssignEdge(nil), in.Edges...)
sort.Slice(edges, func(a, b int) bool {
if edges[a].CandidateIdx != edges[b].CandidateIdx {
return edges[a].CandidateIdx < edges[b].CandidateIdx
}
return edges[a].RequirementIdx < edges[b].RequirementIdx
})
type ref struct {
node, idx, candIdx, reqIdx int
value int64
}
var refs []ref
for _, e := range edges {
if e.Value <= 0 || e.CandidateIdx < 0 || e.CandidateIdx >= C || e.RequirementIdx < 0 || e.RequirementIdx >= R {
continue // only profitable, in range, qualified edges
}
from := candNode(e.CandidateIdx)
idx := len(g.graph[from])
g.addEdge(from, reqNode(e.RequirementIdx), 1, -e.Value)
refs = append(refs, ref{node: from, idx: idx, candIdx: e.CandidateIdx, reqIdx: e.RequirementIdx, value: e.Value})
}
for j := 0; j < R; j++ {
if in.Slots[j] > 0 {
g.addEdge(reqNode(j), sink, in.Slots[j], 0)
}
}
if err := g.solveMaxProfit(ctx, source, sink); err != nil {
return PortfolioSolution{}, err
}
var sol PortfolioSolution
for _, r := range refs {
if g.graph[r.node][r.idx].cap == 0 { // the unit was pushed: the pair is assigned
sol.Assignments = append(sol.Assignments, Assignment{CandidateIdx: r.candIdx, RequirementIdx: r.reqIdx, Value: r.value})
sol.TotalValue += r.value
}
}
sort.Slice(sol.Assignments, func(a, b int) bool {
if sol.Assignments[a].CandidateIdx != sol.Assignments[b].CandidateIdx {
return sol.Assignments[a].CandidateIdx < sol.Assignments[b].CandidateIdx
}
return sol.Assignments[a].RequirementIdx < sol.Assignments[b].RequirementIdx
})
return sol, nil
}
const infCost int64 = 1 << 62
type mcmfEdge struct {
to, rev int
cap int
cost int64
}
type mcmf struct {
graph [][]mcmfEdge
}
func newMCMF(n int) *mcmf { return &mcmf{graph: make([][]mcmfEdge, n)} }
func (g *mcmf) addEdge(from, to, capacity int, cost int64) {
g.graph[from] = append(g.graph[from], mcmfEdge{to: to, rev: len(g.graph[to]), cap: capacity, cost: cost})
g.graph[to] = append(g.graph[to], mcmfEdge{to: from, rev: len(g.graph[from]) - 1, cap: 0, cost: -cost})
}
// solveMaxProfit augments unit flow along minimum cost paths while each adds profit, stopping when the
// cheapest augmenting path no longer reduces cost. It checks the context each iteration so it never hangs.
func (g *mcmf) solveMaxProfit(ctx context.Context, s, t int) error {
n := len(g.graph)
pot := make([]int64, n)
for i := range pot {
pot[i] = infCost
}
pot[s] = 0
// Bellman Ford initial potentials (cost edges are negative under maximization). O(V*E), once.
for iter := 0; iter < n-1; iter++ {
changed := false
for u := 0; u < n; u++ {
if pot[u] == infCost {
continue
}
for _, e := range g.graph[u] {
if e.cap > 0 && pot[u]+e.cost < pot[e.to] {
pot[e.to] = pot[u] + e.cost
changed = true
}
}
}
if !changed {
break
}
}
// Nodes unreachable from the source have no incoming residual edge from the reachable set, so they are
// never traversed; pinning their potential to 0 keeps the arithmetic clean without affecting the solve.
for i := range pot {
if pot[i] == infCost {
pot[i] = 0
}
}
dist := make([]int64, n)
parentNode := make([]int, n)
parentEdge := make([]int, n)
for {
select {
case <-ctx.Done():
return ErrSolveBudget
default:
}
for i := range dist {
dist[i] = infCost
}
dist[s] = 0
pq := &nodePQ{{node: s, dist: 0}}
for pq.Len() > 0 {
it := heap.Pop(pq).(pqItem)
if it.dist > dist[it.node] {
continue
}
u := it.node
for ei := range g.graph[u] {
e := g.graph[u][ei]
if e.cap <= 0 {
continue
}
rc := e.cost + pot[u] - pot[e.to] // reduced cost, non negative by the potential invariant
nd := dist[u] + rc
if nd < dist[e.to] {
dist[e.to] = nd
parentNode[e.to] = u
parentEdge[e.to] = ei
heap.Push(pq, pqItem{node: e.to, dist: nd})
}
}
}
if dist[t] == infCost {
break // no augmenting path remains
}
for v := 0; v < n; v++ {
if dist[v] < infCost {
pot[v] += dist[v]
}
}
// pot[s] stays 0, so pot[t] is the real cost of the cheapest augmenting path (the marginal cost of
// the next unit). Stop as soon as it is non negative: no further augmentation can add profit.
if pot[t]-pot[s] >= 0 {
break
}
// Augment one unit along the path (the bottleneck is 1, bounded by the source to candidate edge).
push := 1 << 30
for v := t; v != s; v = parentNode[v] {
if c := g.graph[parentNode[v]][parentEdge[v]].cap; c < push {
push = c
}
}
for v := t; v != s; v = parentNode[v] {
pn, pe := parentNode[v], parentEdge[v]
g.graph[pn][pe].cap -= push
g.graph[v][g.graph[pn][pe].rev].cap += push
}
}
return nil
}
// AssignCounterfactual is the value matrix explanation for one assignment: where else this candidate could
// best go and who else could best serve this requirement, holding the rest of the solution fixed. Indices
// are -1 when there is no alternative. This is the interpretable explanation, NOT a true linear program re
// solve marginal; the two can differ, and the interpretable next best is the defensible choice for an AEDT
// because a reviewer can verify it directly against the value matrix.
type AssignCounterfactual struct {
CandidateNextReqIdx int
CandidateValueDelta int64
RequirementNextCandIdx int
RequirementValueDelta int64
}
// Counterfactuals computes, for each assignment, the candidate's next best requirement and the
// requirement's next best candidate by inspecting the value matrix. Pure and deterministic (ties break to
// the smaller index).
func Counterfactuals(in PortfolioInput, sol PortfolioSolution) map[int]AssignCounterfactual {
byCand := map[int][]AssignEdge{}
byReq := map[int][]AssignEdge{}
for _, e := range in.Edges {
if e.Value <= 0 {
continue
}
byCand[e.CandidateIdx] = append(byCand[e.CandidateIdx], e)
byReq[e.RequirementIdx] = append(byReq[e.RequirementIdx], e)
}
out := make(map[int]AssignCounterfactual, len(sol.Assignments))
for _, a := range sol.Assignments {
cf := AssignCounterfactual{CandidateNextReqIdx: -1, RequirementNextCandIdx: -1}
bestV := int64(-1)
for _, e := range byCand[a.CandidateIdx] {
if e.RequirementIdx == a.RequirementIdx {
continue
}
if e.Value > bestV || (e.Value == bestV && e.RequirementIdx < cf.CandidateNextReqIdx) {
bestV = e.Value
cf.CandidateNextReqIdx = e.RequirementIdx
}
}
if cf.CandidateNextReqIdx != -1 {
cf.CandidateValueDelta = a.Value - bestV
}
bestV2 := int64(-1)
for _, e := range byReq[a.RequirementIdx] {
if e.CandidateIdx == a.CandidateIdx {
continue
}
if e.Value > bestV2 || (e.Value == bestV2 && e.CandidateIdx < cf.RequirementNextCandIdx) {
bestV2 = e.Value
cf.RequirementNextCandIdx = e.CandidateIdx
}
}
if cf.RequirementNextCandIdx != -1 {
cf.RequirementValueDelta = a.Value - bestV2
}
out[a.CandidateIdx] = cf
}
return out
}
type pqItem struct {
node int
dist int64
}
// nodePQ is a min heap keyed by (dist, node id). The node id tiebreak makes the settle order, and thus the
// chosen augmenting path and the final assignment, fully deterministic.
type nodePQ []pqItem
func (p nodePQ) Len() int { return len(p) }
func (p nodePQ) Less(i, j int) bool {
if p[i].dist != p[j].dist {
return p[i].dist < p[j].dist
}
return p[i].node < p[j].node
}
func (p nodePQ) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p *nodePQ) Push(x any) { *p = append(*p, x.(pqItem)) }
func (p *nodePQ) Pop() any {
old := *p
n := len(old)
it := old[n-1]
*p = old[:n-1]
return it
}