Skip to content

Commit

Permalink
Optimize Shapley value computation in the non-probabilistic case
Browse files Browse the repository at this point in the history
  • Loading branch information
PierreSenellart committed Nov 7, 2023
1 parent 3dab908 commit 7e63b10
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 8 deletions.
4 changes: 4 additions & 0 deletions src/BooleanCircuit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,17 @@ gate_t BooleanCircuit::setGate(const uuid &u, BooleanGate type)
gate_t BooleanCircuit::setGate(const uuid &u, BooleanGate type, double p)
{
auto id = setGate(u, type);
if(std::isnan(p))
p=1.;
setProb(id,p);
return id;
}

gate_t BooleanCircuit::setGate(BooleanGate type, double p)
{
auto id = setGate(type);
if(std::isnan(p))
p=1.;
setProb(id,p);
return id;
}
Expand Down
6 changes: 6 additions & 0 deletions src/BooleanCircuit.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ std::set<gate_t> inputs;
std::set<gate_t> mulinputs;
std::vector<double> prob;
std::map<gate_t, unsigned> info;
bool probabilistic=false;

public:
BooleanCircuit() {
Expand All @@ -40,11 +41,16 @@ gate_t setGate(const uuid &u, BooleanGate t) override;
gate_t setGate(BooleanGate t, double p);
gate_t setGate(const uuid &u, BooleanGate t, double p);
void setProb(gate_t g, double p) {
if(!probabilistic && p!=1.)
probabilistic=true;
prob[static_cast<std::underlying_type<gate_t>::type>(g)]=p;
}
double getProb(gate_t g) const {
return prob[static_cast<std::underlying_type<gate_t>::type>(g)];
}
bool isProbabilistic() const {
return probabilistic;
}
void setInfo(gate_t g, unsigned info);
unsigned getInfo(gate_t g) const;

Expand Down
22 changes: 15 additions & 7 deletions src/dDNNF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ double dDNNF::probabilityEvaluation() const

std::unordered_map<gate_t, std::vector<double> > dDNNF::shapley_delta(gate_t root) const {
std::unordered_map<gate_t, std::vector<double> > result;

if(!isProbabilistic())
return result;

// Stack to simulate recursion: contains a pair (node, b) where b
// indicates whether this is the beginning (false) or ending (true) of
// the processing of a node
Expand Down Expand Up @@ -317,10 +321,11 @@ std::vector<std::vector<double> > dDNNF::shapley_alpha(gate_t root) const {
stack.push(std::make_pair(getWires(node)[0], false));
} else {
result[node] = result[getWires(node)[0]];
for(unsigned k=0; k<result[node].size(); ++k)
auto k0=isProbabilistic()?0:result[node].size()-1;
for(unsigned k=k0; k<result[node].size(); ++k)
for(unsigned l=0; l<=k; ++l) {
result[node][k][l] *= -1;
result[node][k][l] += comb(k,l)*delta[node][k];
result[node][k][l] += comb(k,l)*(isProbabilistic()?delta[node][k]:1);
}
}
break;
Expand All @@ -338,7 +343,8 @@ std::vector<std::vector<double> > dDNNF::shapley_alpha(gate_t root) const {
result[node] = result[getWires(node)[0]];
for(size_t i=1; i<getWires(node).size(); ++i) {
const auto &r = result[getWires(node)[i]];
for(unsigned k=0; k<r.size(); ++k)
auto k0=isProbabilistic()?0:r.size()-1;
for(unsigned k=k0; k<r.size(); ++k)
for(unsigned l=0; l<r[k].size(); ++l)
result[node][k][l]+=r[k][l];
}
Expand All @@ -364,7 +370,8 @@ std::vector<std::vector<double> > dDNNF::shapley_alpha(gate_t root) const {
const auto n1=r1.size()-1;
const auto n2=r2.size()-1;
result[node].resize(n1+n2+1);
for(size_t k=0; k<=n1+n2; ++k) {
auto k0=isProbabilistic()?0:n1+n2;
for(size_t k=k0; k<=n1+n2; ++k) {
result[node][k].resize(k+1);
for(size_t l=0; l<=k; ++l) {
for(size_t k1=std::max(0,static_cast<int>(k-n2)); k1<=std::min(k,n1); ++k1)
Expand Down Expand Up @@ -396,10 +403,11 @@ double dDNNF::shapley(gate_t var) const {

double result=0.;

for(size_t k=0; k<std::max(alpha_pos.size(),alpha_neg.size()); ++k)
double k0=isProbabilistic()?0:alpha_pos.size()-1;
for(size_t k=k0; k<alpha_pos.size(); ++k)
for(size_t l=0; l<=k; ++l) {
double pos = k>=alpha_pos.size()?0.:alpha_pos[k][l];
double neg = k>=alpha_neg.size()?0.:alpha_neg[k][l];
double pos = alpha_pos[k][l];
double neg = alpha_neg[k][l];
result += (pos-neg)/comb(k,l)/(k+1);
}

Expand Down
2 changes: 1 addition & 1 deletion src/dDNNF.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ mutable std::unordered_map<gate_t, double, hash_gate_t> probability_cache;
std::unordered_map<gate_t, std::vector<double> > shapley_delta(gate_t root) const;
std::vector<std::vector<double> > shapley_alpha(gate_t root) const;
std::vector<gate_t> topological_order(const std::vector<std::vector<gate_t> > &reversedWires) const;
gate_t root;
gate_t root{0};

public:
gate_t getRoot() const {
Expand Down
16 changes: 16 additions & 0 deletions test/expected/shapley.out
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,19 @@
Nancy | Paris | 0.156
(7 rows)

remove_provenance
-------------------

(1 row)

name | city | shapley
----------+----------+---------
Ellen | Berlin | -0.036
Susan | Berlin | -0.036
John | New York | -0.036
Paul | New York | -0.036
Dave | Paris | 0.048
Magdalen | Paris | 0.048
Nancy | Paris | 0.048
(7 rows)

32 changes: 32 additions & 0 deletions test/sql/shapley.sql
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,35 @@ SELECT name, city, ROUND(shapley::numeric,3) AS shapley FROM shapley_result
ORDER BY city, name;

DROP TABLE shapley_result;

-- Shapley computation in the non-probabilistic case
DO $$ BEGIN
PERFORM set_prob(provenance(), 1.) FROM personnel;
END $$;

CREATE TABLE shapley_result AS
SELECT name, city, shapley(c.provenance,p.provenance) FROM (
SELECT provenance() from (SELECT DISTINCT 1 FROM (
(SELECT DISTINCT city FROM personnel)
EXCEPT
(SELECT p1.city
FROM personnel p1, personnel p2
WHERE p1.city = p2.city AND p1.id < p2.id
GROUP BY p1.city
ORDER BY p1.city)
) t
) u)
AS c,
(SELECT *, provenance() FROM personnel) AS p;

SELECT remove_provenance('shapley_result');

SELECT name, city, ROUND(shapley::numeric,3) AS shapley FROM shapley_result
ORDER BY city, name;

DROP TABLE shapley_result;

-- Put back original probability values
DO $$ BEGIN
PERFORM set_prob(provenance(), id*1./10) FROM personnel;
END $$;

0 comments on commit 7e63b10

Please sign in to comment.