Polynomial circuit-local Shapley attribution for single boolean circuit samples.
Project description
shapley-value-for-circuit
Polynomial circuit-local Shapley attribution for a single boolean circuit sample.
This package uses
circuit-static-description
to load, parse, and validate circuits. Given a circuit, a complete input sample,
and one output index, it returns the attribution of that output to each original
input.
Installation
pip install -e .
For development and tests:
pip install -e .[test]
pytest
For heatmap visualization:
pip install -e .[viz]
Quick Start
from circuit_static_description import Circuit
from shapley_value_for_circuit import explain, shapley_values
circuit = Circuit(
2, # Number of inputs.
1, # Number of outputs.
outputs=["AND(I0, I1)"],
)
result = explain(circuit, inputs=[1, 1], output=0)
print(result.values)
# Attribution for each original input:
# I0 contributes 1/2, and I1 contributes 1/2.
# (Fraction(1, 2), Fraction(1, 2))
print(result.base_value, result.output_value)
# The target output is 0 on the all-zero baseline input,
# and 1 on the current input [1, 1].
# 0 1
print(shapley_values(circuit, [1, 1], as_float=True))
# Same attributions returned as floats.
# [0.5, 0.5]
You can also pass circuit text in the circuit-static-description format:
from shapley_value_for_circuit import explain
text = """
INPUTS 3
OUTPUTS 1
V0 = AND(I0, I1)
OUT0 = OR(V0, I2)
"""
result = explain(text, inputs=[1, 1, 0], output="OUT0")
print(result.values)
# I0 and I1 explain V0 = AND(I0, I1).
# I2 is unchanged from the baseline, so its contribution is 0.
# (Fraction(1, 2), Fraction(1, 2), Fraction(0, 1))
Intuition
The circuit output is boolean, so result.base_value and
result.output_value are always either 0 or 1. The attribution values are
not restricted to booleans: they explain how the output changed from the
baseline sample to the current sample.
With the default all-zero baseline, AND(I0, I1) on input [1, 1] gives:
base_value = 0
# Output of AND(0, 0) on the all-zero baseline input.
output_value = 1
# Output of AND(1, 1) on the current input.
values = [1 / 2, 1 / 2]
# I0 contributes 1/2.
# I1 contributes 1/2.
base_value + sum(values) == output_value
# 0 + 1/2 + 1/2 == 1
Both inputs are needed to turn the output on, so the local credit is split equally.
OR(I0, I1) on input [1, 1] also gives:
base_value = 0
# Output of OR(0, 0) on the all-zero baseline input.
output_value = 1
# Output of OR(1, 1) on the current input.
values = [1 / 2, 1 / 2]
# I0 contributes 1/2.
# I1 contributes 1/2.
Either input would be enough by itself, so the redundant positive effect is split between them.
AND(I0, I1) on input [1, 0] gives:
base_value = 0
# Output of AND(0, 0) on the all-zero baseline input.
output_value = 0
# Output of AND(1, 0) on the current input.
values = [0, 0]
# I0 changed from 0 to 1, but the target output stayed 0.
# I1 stayed at 0.
The first input changed, but the requested output did not change from the baseline, so there is no output change to attribute.
NOT(I0) on input [1] gives:
base_value = 1
# Output of NOT(0) on the all-zero baseline input.
output_value = 0
# Output of NOT(1) on the current input.
values = [-1]
# I0 contributes -1 because changing I0 from 0 to 1 turns the output off.
base_value + sum(values) == output_value
# 1 + (-1) == 0
The input change turns the output off, so the attribution is negative.
API
explain(circuit, inputs, output=0, *, baseline_inputs=None) -> ShapleyResult
circuit may be one of:
circuit_static_description.Circuit- circuit text
- a circuit file path
baseline_inputs is the reference sample. By default, it is the all-zero input.
You can also pass one boolean value for all inputs, or a sequence with the same
length as the circuit input count.
shapley_values(circuit, inputs, output=0, *, baseline_inputs=None, as_float=False)
calculate_shapley_values(...) # Alias for shapley_values.
Useful ShapleyResult fields:
values: one attribution per input, represented exactly asfractions.Fractionoutput_value: output value for the current samplebase_value: output value for the baseline sampleinput_values: normalized current sample inputsbaseline_input_values: normalized baseline inputs
The result always satisfies:
result.base_value + sum(result.values) == result.output_value
Heatmap Visualization
You can fold a one-dimensional local Shapley vector into rows with K columns
and visualize it as a heatmap. This is useful when the circuit inputs originally
represent a grid, image, board, or any other fixed-width layout.
from circuit_static_description import Circuit
from shapley_value_for_circuit import (
explain,
fold_shapley_values,
save_shapley_heatmap,
show_shapley_heatmap,
)
circuit = Circuit(
6, # Number of inputs.
1, # Number of outputs.
outputs=["OR(AND(I0, I1), AND(I4, I5))"],
)
result = explain(circuit, inputs=[1, 1, 0, 0, 1, 1], output=0)
matrix = fold_shapley_values(result, columns=3)
# Fold the 6 input attributions into 2 rows and 3 columns:
# [
# [phi_I0, phi_I1, phi_I2],
# [phi_I3, phi_I4, phi_I5],
# ]
save_shapley_heatmap(
result,
columns=3,
path="local_shapley_heatmap.png",
title="Local Shapley values",
annotate=True,
)
# Save the heatmap image to local_shapley_heatmap.png.
show_shapley_heatmap(result, columns=3)
# Open an interactive matplotlib window for direct viewing.
For lower-level control, use plot_shapley_heatmap(...), which returns
(fig, ax) so you can further customize the matplotlib figure before saving or
showing it.
Algorithm
This package computes circuit-local Shapley propagation. It does not enumerate all input subsets and does not compute the exponential global Shapley value.
The algorithm is:
- Each original input starts with its change from the baseline.
- Each logic gate is explained by the exact Shapley values of its unary or binary local game.
- Each gate-level attribution is propagated through the upstream attribution vectors until it reaches the original inputs.
Supported circuit-static-description gates:
ANDORNOTXORNANDNOR
The runtime is polynomial. If n is the input count and g is the number of
gates and variables reachable from the requested output, the dense worst case is
approximately O(g * n), because each intermediate node may carry an attribution
vector over all original inputs. Sparse cases are usually smaller.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shapley_value_for_circuit-0.1.1.tar.gz.
File metadata
- Download URL: shapley_value_for_circuit-0.1.1.tar.gz
- Upload date:
- Size: 11.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.4.1 CPython/3.11.15 Windows/10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e7a1bb3c5abdd90265af48e10cea4ae329a222c521ea29b54f790e8a666b0dba
|
|
| MD5 |
b6d3c3475a5bc2b0bd4913f994835ff4
|
|
| BLAKE2b-256 |
1afd32c51026938ecf9101acaf1cb1a97627d120f8634a0fc00f65bb7ae7fa04
|
File details
Details for the file shapley_value_for_circuit-0.1.1-py3-none-any.whl.
File metadata
- Download URL: shapley_value_for_circuit-0.1.1-py3-none-any.whl
- Upload date:
- Size: 9.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.4.1 CPython/3.11.15 Windows/10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cee15a56668f1b1b3ca35c9f7dc6d2d32026ca3ddb6606d8873c0b686a3a55b5
|
|
| MD5 |
9ab172b2b8d03a699e87e45bf500005e
|
|
| BLAKE2b-256 |
0805c9a7e26ab7c67b3faf632984027a7e09d469408179d614b23846ac5585b5
|