def __mul__(self, right): """ A true multiplication of two potentials would be defined as X * Y = Z where the sets of variables z = x U y. We would then identify the instantiations of x and y that are consistent with z and Z(z) = X(x)Y(y). We are generally going to be multiplying sepset potentials by clique potentials where the variables of a setpset potential are a subset of the variables of the clique. Therefore we are going to assume in this operation that right's variables are a subset of self's. """ # right should only be a DiscreteDistribution or a ContinuousDistribution if it is a subset and it should be __imul__ assert(not isinstance(right, DiscreteDistribution) and not isinstance(right, ConditionalDiscreteDistribution)), \ "Attempt to Multiply Potential with incompatible type: Discrete or Conditional" if isinstance(right, (int, float, complex, long)): potential = copy.deepcopy(self) potential.table *= right else: nodeSet = self.__nodeSet_.union(right.__nodeSet_) potential = Potential(list(nodeSet)) selfValues = [potential.nodes.index(node) for node in self.nodes] rightValues = [potential.nodes.index(node) for node in right.nodes] # Store the following lists so we don't have to recompute them on every iteration potAxes = range(potential.nDims) selfAxes = range(self.nDims) rightAxes = range(right.nDims) #OPTIMIZE: Should be able to do this without blindly iterating through dimensions. for seq in Utilities.sequence_generator(potential.dims): #OPTIMIZE: Could access the table directly, but would break down our abstraction potIndex = potential.generate_index(seq, potAxes) selfIndex = self.generate_index(seq[selfValues], selfAxes) rightIndex = right.generate_index(seq[rightValues], rightAxes) potential[potIndex] = self[selfIndex] * right[rightIndex] return potential
def marginalize(self, other): """ Return a new potential that is the marginalization of this potential given other. This identifies the instantiations of self (s1,s2,...,sn) that are consistent with other and sum self(s1) + self(s2) + ... + self(sn). """ new = copy.deepcopy(other) intersect = self.__nodeSet_.intersection(new.__nodeSet_) newAxes = range(new.nDims) sequence = Utilities.sequence_generator(other.dims) for seq in sequence: index = self.generate_index_node(seq, intersect) newIndex = new.generate_index(seq, newAxes) val = self[index] if isinstance(val, ndarray): val = val.sum() new[newIndex] = val return new
def __imul__(self, right): """ This is the same operation as __mul__ except that if right.nodes is a subset of self.nodes, we do the multiplication in place, because there is no reason to make a copy, which wastes time and space. """ if isinstance(right, (int, float, complex, long)): self.table *= right # FIXME: should be right.__nodeSet_ but doesn't work when right is DiscreteDistribution elif self.__nodeSet_.issuperset(right.nodes): #OPTIMIZE: There must be a way to do this without iterating over every value of table selfAxes = [self.nodes.index(node) for node in right.nodes] rightAxes = range(right.nDims) for seq in Utilities.sequence_generator(right.dims): selfIndex = self.generate_index(seq, selfAxes) #OPTIMIZE: Could index right.table directly, but this upholds our abstraction barrier rightIndex = right.generate_index(seq, rightAxes) self[selfIndex] *= right[rightIndex] else: """ If potential will be over a different set of variables after multiplication, might as well use full __mul__ version, which copies. """ self = self.__mul__(right) return self