def __init__(self, sources, expression): self.objects = {} # the list of known terms # make all the module outputs as known terms for name in sources: self.objects[name] = Source(name) # handle the term 'dot(a, b)' to mean DotProduct(a, b) self.objects["dot"] = DotProduct # use Python's eval to do the parsing of expressions for us self.validate_string(expression) sanitized_exp = " ".join(expression.split("\n")) try: self.expression = eval(sanitized_exp, {}, self) except NameError as e: raise SpaParseError( "Unknown module in expression '%s': %s" % (expression, e) ) except TypeError as e: raise SpaParseError( "Invalid operator in expression '%s': %s" % (expression, e) ) # normalize the result to a summation if not isinstance(self.expression, Summation): self.expression = Summation([self.expression])
def parse(self, text): """Evaluate a text string and return the corresponding SemanticPointer. This uses the Python ``eval()`` function, so any Python operators that have been defined for SemanticPointers are valid (``+``, ``-``, ``*``, ``~``, ``()``). Any terms do not exist in the vocabulary will be automatically generated. Valid semantic pointer terms must start with a capital letter. If the expression returns a scalar (int or float), a scaled version of the identity SemanticPointer will be returned. """ # The following line does everything. Note that self is being # passed in as the locals dictionary, and thanks to the __getitem__ # implementation, this will automatically create new semantic # pointers as needed. try: value = eval(text, {}, self) except NameError: raise SpaParseError( "Semantic pointers must start with a capital letter.") if is_number(value): value = value * self.identity if not isinstance(value, pointer.SemanticPointer): raise SpaParseError( "The result of parsing '%s' is not a SemanticPointer" % text) return value
def add(self, key, p): """Add a new semantic pointer to the vocabulary. The pointer value can be a `.SemanticPointer` or a vector. """ if self.readonly: raise ReadonlyError(attr='Vocabulary', msg="Cannot add semantic pointer '%s' to " "read-only vocabulary." % key) if not key[0].isupper(): raise SpaParseError( "Semantic pointers must begin with a capital letter.") if not isinstance(p, pointer.SemanticPointer): p = pointer.SemanticPointer(p) if key in self.pointers: raise ValidationError("The semantic pointer %r already exists" % key, attr='pointers', obj=self) self.pointers[key] = p self.keys.append(key) self.vectors = np.vstack([self.vectors, p.v]) # Generate vector pairs if self.include_pairs and len(self.keys) > 1: for k in self.keys[:-1]: self.key_pairs.append('%s*%s' % (k, key)) v = (self.pointers[k] * p).v self.vector_pairs = np.vstack([self.vector_pairs, v])
def validate_string(self, text): m = re.search("~[^a-zA-Z]", text) if m is not None: raise SpaParseError( "~ is only permitted before names (e.g., DOG) " "or modules (e.g., vision): %s" % text )
def __init__(self, sources, sinks, effect): self.effect = OrderedDict() # Splits by ',' and separates into lvalue=rvalue. We cannot simply use # split, because the rvalue may contain commas in the case of dot(*,*). # However, *? is lazy, and * is greedy, making this regex work. for lvalue, rvalue in re.findall("(.*?)=([^=]*)(?:,|$)", effect): sink = lvalue.strip() if sink not in sinks: raise SpaParseError( "Left-hand module '%s' from effect '%s=%s' " "is not defined." % (lvalue, lvalue, rvalue)) if sink in self.effect: raise SpaParseError( "Left-hand module '%s' from effect '%s=%s' " "is assigned to multiple times in '%s'." % (lvalue, lvalue, rvalue, effect)) self.effect[sink] = Expression(sources, rvalue)
def __init__(self, item1, item2, scale=1.0): if isinstance(item1, (int, float)): item1 = Symbol(item1) if isinstance(item2, (int, float)): item2 = Symbol(item2) if not isinstance(item1, (Source, Symbol)): raise SpaParseError("The first item in the dot product is not a " "semantic pointer or a spa.Module output.") if not isinstance(item2, (Source, Symbol)): raise SpaParseError("The second item in the dot product is not a " "semantic pointer or a spa.Module output.") if not isinstance(item1, Source) and not isinstance(item2, Source): raise SpaParseError("One of the two terms for the dot product " "must be a spa.Module output.") self.item1 = item1 self.item2 = item2 self.scale = float(scale)
def __getitem__(self, key): # this gets used by the eval in the constructor to create new # terms as needed item = self.objects.get(key, None) if item is None: if not key[0].isupper(): raise SpaParseError( "Semantic pointers must begin with a capital letter.") item = Symbol(key) self.objects[key] = item return item
def __getitem__(self, key): """Return the semantic pointer with the requested name. If one does not exist, automatically create one. The key must be a valid semantic pointer name, which is any Python identifier starting with a capital letter. """ if not key[0].isupper(): raise SpaParseError( "Semantic pointers must begin with a capital letter.") value = self.pointers.get(key, None) if value is None: if is_iterable(self.unitary): unitary = key in self.unitary else: unitary = self.unitary value = self.create_pointer(unitary=unitary) self.add(key, value) return value
def __invert__(self): if self.transform.symbol != '1': raise SpaParseError( "You can only invert sources without transforms") return Source(self.name, self.transform, not self.inverted)