DooABLe / src /dooable /metrics.py
pranamanam's picture
Upload 309 files
81ae663 verified
Raw
History Blame Contribute Delete
1.56 kB
"""Exact graph statistics and candidate-level molecular measurements."""
import numpy as np
from .exact import endpoint_distribution, expected_cost, solve
def graph_metrics(graph, forward, log_rewards, temperature):
"""Return exact endpoint TV, joint KL and mean cost on the stored DAG."""
ref = solve(graph, log_rewards, temperature)
p = endpoint_distribution(graph, forward)
mass = np.zeros(len(graph.nodes))
mass[graph.root_index] = 1.0
joint_kl = 0.0
for u in graph.order:
for i in graph.outgoing[u]:
f = mass[u] * forward[i]
if f > 0:
joint_kl += f * np.log(forward[i] / ref.forward[i])
mass[graph.node_index[graph.edges[i].target]] += f
endpoint_kl = sum(p[y] * np.log(p[y] / ref.target[y]) for y in p if p[y] > 0)
return {
"endpoint_tv": 0.5 * sum(abs(p[y] - ref.target[y]) for y in p),
"joint_kl": float(joint_kl),
"conditional_kl": max(0.0, float(joint_kl - endpoint_kl)),
"mean_cost": expected_cost(graph, forward),
"conditional_free_energy_gap": temperature
* max(0.0, float(joint_kl - endpoint_kl)),
}
def hypervolume_2d(points):
"""Dominated area for utility points clipped to [0,1]^2, reference (0,0)."""
a = np.clip(np.asarray(points, dtype=float), 0, 1)
if a.size == 0:
return 0.0
a = a[np.argsort(-a[:, 0])]
area = 0.0
height = 0.0
for x, y in a:
if y > height:
area += x * (y - height)
height = y
return float(area)