| """Changes to route costs and endpoint merging on an identical action graph.""" |
|
|
| from dataclasses import replace |
| from .graph import Graph, Node |
| from .exact import solve |
|
|
|
|
| def zero_cost_policy(graph, log_rewards): |
| """Compute the exact policy after setting every route cost to zero.""" |
| altered = Graph( |
| graph.nodes, |
| [replace(e, cost=0.0) for e in graph.edges], |
| graph.root, |
| graph.metadata, |
| ) |
| return solve(altered, log_rewards, 1.0).forward |
|
|
|
|
| def duplicate_endpoint_policy(graph, log_rewards, temperature): |
| """Assign a separate reward to each incoming terminal edge. |
| |
| Edge ordering and all nonterminal nodes are retained. The returned policy |
| can be evaluated on the original graph to measure canonical endpoint bias. |
| """ |
| nodes = [n for n in graph.nodes if n.outcome is None] |
| edges = [] |
| rewards = {} |
| for index, edge in enumerate(graph.edges): |
| destination = graph.nodes[graph.node_index[edge.target]] |
| if destination.outcome is None: |
| edges.append(edge) |
| continue |
| outcome = f"copy_{index}" |
| node_id = f"terminal_copy_{index}" |
| nodes.append(Node(node_id, outcome, destination.features)) |
| edges.append(replace(edge, target=node_id)) |
| rewards[outcome] = log_rewards[destination.outcome] |
| altered = Graph(nodes, edges, graph.root, graph.metadata) |
| return solve(altered, rewards, temperature).forward |
|
|