Add one relationship to the extent

        origin - origin of the relationship (similar to an RDF subject)
        rel - type IRI of the relationship (similar to an RDF predicate)
        target - target of the relationship (similar to an RDF object), a boolean, floating point or unicode object
        attrs - optional attribute mapping of relationship metadata, i.e. {attrname1: attrval1, attrname2: attrval2}
        index - optional position for the relationship to be inserted

def add(self, origin, rel, target, attrs=None, index=None):
        '''
        Add one relationship to the extent

        origin - origin of the relationship (similar to an RDF subject)
        rel - type IRI of the relationship (similar to an RDF predicate)
        target - target of the relationship (similar to an RDF object), a boolean, floating point or unicode object
        attrs - optional attribute mapping of relationship metadata, i.e. {attrname1: attrval1, attrname2: attrval2}
        index - optional position for the relationship to be inserted
        '''
        #FIXME: return an ID (IRI) for the resulting relationship?

        if not origin: 
            raise ValueError('Relationship origin cannot be null')
        if not rel: 
            raise ValueError('Relationship ID cannot be null')

        # convert attribute class to the expected type
        if type(attrs) != type(self._attr_cls):
            attrs = self._attr_cls(attrs or {})

        #No, could be an I instance, fails assertion
        #assert isinstance(origin, str) and isinstance(origin, str) and isinstance(origin, str) and isinstance(origin, dict), (origin, rel, target, attrs)

        item = (origin, rel, target, attrs)
        if index is not None:
            rid = index
            self._relationships.insert(index, item)
        else:
            rid = self.size()
            self._relationships.append(item)
        return rid