Exemplo n.º 1
0
    def checkTheWorld(self):
        # Test constructors
        u = PersistentList()
        u0 = PersistentList(l0)
        u1 = PersistentList(l1)
        u2 = PersistentList(l2)

        uu = PersistentList(u)
        uu0 = PersistentList(u0)
        uu1 = PersistentList(u1)
        uu2 = PersistentList(u2)

        v = PersistentList(tuple(u))

        class OtherList:
            def __init__(self, initlist):
                self.__data = initlist

            def __len__(self):
                return len(self.__data)

            def __getitem__(self, i):
                return self.__data[i]

        v0 = PersistentList(OtherList(u0))
        vv = PersistentList("this is also a sequence")

        # Test __repr__
        eq = self.assertEqual

        eq(str(u0), str(l0), "str(u0) == str(l0)")
        eq(repr(u1), repr(l1), "repr(u1) == repr(l1)")
        eq(repr(u2), repr(l2), "repr(u2) == repr(l2)")

        # Test __cmp__ and __len__

        # Py3: No cmp() or __cmp__ anymore.
        if PY2:

            def mycmp(a, b):
                r = cmp(a, b)
                if r < 0: return -1
                if r > 0: return 1
                return r

            all = [l0, l1, l2, u, u0, u1, u2, uu, uu0, uu1, uu2]
            for a in all:
                for b in all:
                    eq(mycmp(a, b), mycmp(len(a), len(b)),
                       "mycmp(a, b) == mycmp(len(a), len(b))")

        # Test __getitem__

        for i in range(len(u2)):
            eq(u2[i], i, "u2[i] == i")

        # Test __setitem__

        uu2[0] = 0
        uu2[1] = 100
        try:
            uu2[2] = 200
        except IndexError:
            pass
        else:
            self.fail("uu2[2] shouldn't be assignable")

        # Test __delitem__

        del uu2[1]
        del uu2[0]
        try:
            del uu2[0]
        except IndexError:
            pass
        else:
            self.fail("uu2[0] shouldn't be deletable")

        # Test __getslice__

        for i in range(-3, 4):
            eq(u2[:i], l2[:i], "u2[:i] == l2[:i]")
            eq(u2[i:], l2[i:], "u2[i:] == l2[i:]")
            for j in range(-3, 4):
                eq(u2[i:j], l2[i:j], "u2[i:j] == l2[i:j]")

        # Test __setslice__

        for i in range(-3, 4):
            u2[:i] = l2[:i]
            eq(u2, l2, "u2 == l2")
            u2[i:] = l2[i:]
            eq(u2, l2, "u2 == l2")
            for j in range(-3, 4):
                u2[i:j] = l2[i:j]
                eq(u2, l2, "u2 == l2")

        uu2 = u2[:]
        uu2[:0] = [-2, -1]
        eq(uu2, [-2, -1, 0, 1], "uu2 == [-2, -1, 0, 1]")
        uu2[0:] = []
        eq(uu2, [], "uu2 == []")

        # Test __contains__
        for i in u2:
            self.assertTrue(i in u2, "i in u2")
        for i in min(u2) - 1, max(u2) + 1:
            self.assertTrue(i not in u2, "i not in u2")

        # Test __delslice__

        uu2 = u2[:]
        del uu2[1:2]
        del uu2[0:1]
        eq(uu2, [], "uu2 == []")

        uu2 = u2[:]
        del uu2[1:]
        del uu2[:1]
        eq(uu2, [], "uu2 == []")

        # Test __add__, __radd__, __mul__ and __rmul__

        #self.assertTrue(u1 + [] == [] + u1 == u1, "u1 + [] == [] + u1 == u1")
        self.assertTrue(u1 + [1] == u2, "u1 + [1] == u2")
        #self.assertTrue([-1] + u1 == [-1, 0], "[-1] + u1 == [-1, 0]")
        self.assertTrue(u2 == u2 * 1 == 1 * u2, "u2 == u2*1 == 1*u2")
        self.assertTrue(u2 + u2 == u2 * 2 == 2 * u2, "u2+u2 == u2*2 == 2*u2")
        self.assertTrue(u2 + u2 + u2 == u2 * 3 == 3 * u2,
                        "u2+u2+u2 == u2*3 == 3*u2")

        # Test append

        u = u1[:]
        u.append(1)
        eq(u, u2, "u == u2")

        # Test insert

        u = u2[:]
        u.insert(0, -1)
        eq(u, [-1, 0, 1], "u == [-1, 0, 1]")

        # Test pop

        u = PersistentList([0, -1, 1])
        u.pop()
        eq(u, [0, -1], "u == [0, -1]")
        u.pop(0)
        eq(u, [-1], "u == [-1]")

        # Test remove

        u = u2[:]
        u.remove(1)
        eq(u, u1, "u == u1")

        # Test count
        u = u2 * 3
        eq(u.count(0), 3, "u.count(0) == 3")
        eq(u.count(1), 3, "u.count(1) == 3")
        eq(u.count(2), 0, "u.count(2) == 0")

        # Test index

        eq(u2.index(0), 0, "u2.index(0) == 0")
        eq(u2.index(1), 1, "u2.index(1) == 1")
        try:
            u2.index(2)
        except ValueError:
            pass
        else:
            self.fail("expected ValueError")

        # Test reverse

        u = u2[:]
        u.reverse()
        eq(u, [1, 0], "u == [1, 0]")
        u.reverse()
        eq(u, u2, "u == u2")

        # Test sort

        u = PersistentList([1, 0])
        u.sort()
        eq(u, u2, "u == u2")

        # Test extend

        u = u1[:]
        u.extend(u2)
        eq(u, u1 + u2, "u == u1 + u2")
Exemplo n.º 2
0
class BodyPartGraph(Persistent):
    """A collection of BodyParts joined by edges."""
    def __init__(self, network_args=None):
        """if network_args is None create empty BPG else create a
        random BPG."""

        log.debug('BodyPartGraph.__init__')
        self.bodyparts = PersistentList()
        self.unrolled = 0
        if not network_args:
            # this is used for unrolled bodypart copies
            self.root = None
        else:
            self.randomInit(network_args)

    def destroy(self):
        for bp in self.bodyparts:
            bp.destroy()

    def step(self):
        for bp in self.bodyparts:
            bp.network.step()
        for bp in self.bodyparts:
            if hasattr(bp, 'motor'):
                bp.motor.step()

    def randomInit(self, network_args):
        while 1:
            # create graph randomly
            del self.bodyparts[:]
            num_bodyparts = random.randint(2, BPG_MAX_NODES)
            log.debug('Creating %d random BodyParts' % (num_bodyparts))
            for _ in range(num_bodyparts):
                bp = BodyPart(network_args)
                self.bodyparts.append(bp)
            # randomly select the root node
            self.root = random.choice(self.bodyparts)
            self.root.isRoot = 1
            root_index = self.bodyparts.index(self.root)
            # possible n^2 connections
            num_connects = random.randint(1, BPG_MAX_EDGES)
            log.debug('creating upto %d random connections', num_connects)
            # Now select randomly and use to create actual connect
            inset = [root_index]
            outset = range(0, root_index) + range(root_index + 1,
                                                  num_bodyparts)
            for _ in range(num_connects):
                # select from inset
                src_i = random.randint(0, len(inset) - 1)
                if not outset:
                    break
                inoutset = inset + outset
                dst_i = random.randint(0, len(inoutset) - 1)
                src = self.bodyparts[inset[src_i]]
                bodyparts_dst_i = inoutset[dst_i]
                dst = self.bodyparts[bodyparts_dst_i]
                src.connectTo(dst)
                # there is no check for an existing edge, so we can get multiple edges between src and dst
                if not bodyparts_dst_i in inset:
                    inset.append(bodyparts_dst_i)
                if bodyparts_dst_i in outset:
                    outset.remove(bodyparts_dst_i)
            u = self.unroll(1)
            if MIN_UNROLLED_BODYPARTS <= len(
                    u.bodyparts) <= MAX_UNROLLED_BODYPARTS:
                self.connectInputNodes()
                self.sanityCheck()
                break

    def getNeighbours(self, bp):
        """Calculate the set of valid neighbour bodyparts of bp

        A bodypart is a neighbour of bp if it is a parent or child in the
        bodypartgraph, or if it is bp itself."""
        assert bp in self.bodyparts
        # find possible sources for connection in this phenotype
        valid_bp_neighbours = [bp]
        # .. all children
        valid_bp_neighbours += [e.child for e in bp.edges]
        # .. parent
        valid_bp_neighbours += [
            p for p in self.bodyparts for e in p.edges if e.child == bp
        ]
        for neighbour in valid_bp_neighbours:
            assert neighbour in self.bodyparts
        log.debug('valid bp neighbours = %s', valid_bp_neighbours)
        return valid_bp_neighbours

    def connectInputNodes(self, sanitycheck=1):
        """Connect all sensory input nodes up to something.

        If the bpg is already unrolled, then it is a phenotype and the results
        won't be backannotated to the genotype input_map. If anything is left
        unconnected, an assert error will be thrown.

        If the bpg isn't already unrolled, then it will be, and any missing
        connections will be randomly selected and backannotated into the
        genotype input_map, so that later calls to unroll and connect will be
        able to succeed in connecting every input node up.
        """
        log.debug('BodyPartGraph.connectInputNodes(self=%s)', self)
        if self.unrolled:
            log.debug('self.unrolled=1')
            backannotate = 0
            p_bpg = self
        else:
            log.debug('self.unrolled=0')
            backannotate = 1
            p_bpg = self.unroll()
        log.debug('p_bpg=%s (bodyparts=%s)' % (p_bpg, p_bpg.bodyparts))
        # find all unconnected nodes
        un = set([(p_dst_bp, p_dst_signal) for p_dst_bp in p_bpg.bodyparts
                  for p_dst_signal in p_dst_bp.network.inputs
                  if not p_dst_signal.externalInputs])
        # and unconnected motors
        un = un.union(
            set([(p_dst_bp, 'MOTOR_%d' % i) for p_dst_bp in p_bpg.bodyparts
                 for i in 0, 1, 2 if not p_dst_bp.motor_input[i]]))

        for (p_dst_bp, p_dst_signal) in un:
            log.debug('UNCONNECTED bp %s signal %s', p_dst_bp, p_dst_signal)
            # find corresponding genotype of this node/motor
            g_bp = p_dst_bp.genotype
            if isinstance(p_dst_signal, node.Node):
                g_dst_signal = g_bp.network[p_dst_bp.network.index(
                    p_dst_signal)]
                assert g_dst_signal in g_bp.network.inputs
            else:
                g_dst_signal = p_dst_signal
            # is there an entry in g_bp.input_map for the target node/motor?
            if not g_bp.input_map.has_key(g_dst_signal):
                g_bp.input_map[g_dst_signal] = PersistentList()
            # are there matching maps for this phenotype topology?
            p_neighbours = p_bpg.getNeighbours(p_dst_bp)
            # find all neighbour bps with valid src bp,signal for this dst in input_map
            matches = [(g_src_bp, g_src_signal, p_src_bp, weight)
                       for (g_src_bp, g_src_signal,
                            weight) in g_bp.input_map[g_dst_signal]
                       for p_src_bp in p_neighbours
                       if p_src_bp.genotype is g_src_bp]

            log.debug('input_map matches = %s', matches)
            p_source = None
            for (g_src_bp, g_src_signal, p_src_bp, weight) in matches:
                log.debug(
                    'using prestored map g_src_bp=%s g_src_signal=%s weight=%f',
                    g_src_bp, g_src_signal, weight)
                # convert genotype src signal to phenotype value
                if type(g_src_signal) is str:
                    p_source = (p_src_bp, g_src_signal, weight)
                    break
                else:
                    # find phenotype src node
                    g_src_index = g_src_bp.network.index(g_src_signal)
                    p_src_node = p_src_bp.network[g_src_index]
                    if isinstance(p_dst_signal, node.Node) and isinstance(
                            p_src_node, node.Node) and p_src_bp == p_dst_bp:
                        continue
                    # assert not two nodes in same bp network
                    assert not (isinstance(p_dst_signal, node.Node)
                                and isinstance(p_src_node, node.Node)) or (
                                    p_src_bp != p_dst_bp)
                    # don't allow an external_input if the connection
                    # already exists internally to the network
                    if not isinstance(
                            p_dst_signal, node.Node
                    ) or p_src_node not in p_dst_signal.inputs:
                        # set source to a phenotype (bp,s)
                        assert p_src_node in p_src_bp.network
                        p_source = (p_src_bp, p_src_node, weight)
                        break
                    log.debug('rejected map - nodes already connected')

            if not p_source:
                # no entry in input_map for this node/motor
                # raise error if we aren't connecting up a genotype bpg
                assert backannotate
                # pick a random (bp, signal) from p_bp and backannotate into g_bp.input_map
                p_src_bp = random.choice(p_neighbours)
                # no direct connects between sensors and motors
                posSrcs = []
                if not isinstance(p_dst_signal, str):
                    posSrcs = ['CONTACT', 'JOINT_0', 'JOINT_1', 'JOINT_2']
                # disallow connects from outnode to innode of same network
                if type(p_dst_signal) == str or p_src_bp != p_dst_bp:
                    posSrcs += p_src_bp.network.outputs
                if isinstance(p_dst_signal, node.Node):
                    for x in posSrcs:
                        assert x not in p_dst_signal.inputs
                    # remove any possible srcs that node is already connected to
                    posSrcs = [
                        x for x in posSrcs if x not in p_dst_signal.inputs
                    ]
                log.debug('possible connects %s <- %s', p_dst_signal, posSrcs)
                p_src_signal = random.choice(posSrcs)
                if isinstance(p_dst_signal, node.Node):
                    assert p_src_signal not in p_dst_signal.inputs
                if isinstance(p_src_signal, node.Node):
                    assert p_src_signal in p_src_bp.network
                weight = random.uniform(-7, 7)
                p_source = (p_src_bp, p_src_signal, weight)

                # find genotype of the chosen phenotype (bp,s)
                g_src_bp = p_src_bp.genotype
                weight = random.uniform(-7, 7)
                if isinstance(p_src_signal, node.Node):
                    # phenotype output node -> genotype output node
                    # (depends on offsets being the same)
                    g_src_signal = g_src_bp.network[p_src_bp.network.index(
                        p_src_signal)]
                    assert g_src_signal in g_src_bp.network
                    genosource = (g_src_bp, g_src_signal, weight)
                else:
                    genosource = (g_src_bp, p_src_signal, weight)

                log.debug('entering %s -> %s into bp.input_map', genosource,
                          g_dst_signal)
                # add to genotype.input_map our backannotated source
                assert (g_dst_signal, genosource) not in g_bp.input_map.items()
                g_bp.input_map[g_dst_signal].append(genosource)
                assert g_bp in [pbp.genotype for pbp in p_bpg.bodyparts]

            # add to signal target.
            if isinstance(p_dst_signal, node.Node):
                (p_src_bp, p_src_signal, weight) = p_source
                if isinstance(p_src_signal, node.Node):
                    assert p_src_signal in p_src_bp.network
                p_dst_signal.addExternalInput(p_src_bp, p_src_signal, weight)
            elif p_dst_signal[:6] == 'MOTOR_':
                i = ord(p_dst_signal[6]) - ord('0')
                assert not p_dst_bp.motor_input[i]
                (sbp, ssig, weight) = p_source
                log.debug('p_bp.motor_input[%d]=(%s,%s)' % (i, sbp, ssig))
                assert sbp in p_bpg.bodyparts
                p_dst_bp.motor_input[i] = p_source
            else:
                assert 0

        log.debug('/connectInputNodes, calling sanityCheck')
        if sanitycheck:
            p_bpg.sanityCheck()

        log.debug('/BodyPartGraph.connectInputNodes')

    def getInputs(self, bp):
        """Return a list of all the external inputs to bodypart bp.

        Returns: [ (targetneuron, (srcbp, signal, weight), ... ]"""

        if self.unrolled:
            s0 = [(neuron, externalInput) for neuron in bp.network.inputs
                  for externalInput in neuron.externalInputs]
            sources = []
            for (n, e) in s0:
                w = None
                if isinstance(n, node.WeightNode):
                    w = n.weights[e]
                (b, s) = e
                sources += [(n, (b, s, w))]
            if bp.joint == 'hinge':
                sources += [('MOTOR_2', bp.motor_input[2])]
            elif bp.joint == 'universal':
                sources += [('MOTOR_0', bp.motor_input[0]),
                            ('MOTOR_1', bp.motor_input[1])]
            elif bp.joint == 'ball':
                sources += [('MOTOR_0', bp.motor_input[0]),
                            ('MOTOR_1', bp.motor_input[1]),
                            ('MOTOR_2', bp.motor_input[2])]
        else:
            sources = [(neuron, src) for neuron in bp.input_map
                       for src in bp.input_map[neuron]]
            # src is (bp,sig,wei)
            # remove invalid motor connections
            if bp.joint == 'hinge':
                invalid = ['MOTOR_0', 'MOTOR_1']
            elif bp.joint == 'universal':
                invalid = ['MOTOR_2']
            elif bp.joint == 'ball':
                invalid = []
            sources = [(n, s) for (n, s) in sources if n not in invalid]
        return sources

    def unroll(self, skipNetwork=0):
        """Returns new BPG, of possibly 0 size.

        The BPG will be unrolled. Each path through the network will
        be traced, and a new cloned body part is made for each
        original. The connectivity of the copy will be the same as the
        original, except the copy will respect upper limits on the
        number of instances of any given body part in a single path,
        and final copy instances of a part will be connected to 'final
        edge' children. No loops are left in the new BPG."""
        log.debug('BodyPartGraph.unroll')
        # we need a count of every bp to make sure we don't loop too many times
        for b in self.bodyparts:
            b._v_instance_count = 0
        for b in self.bodyparts:
            assert b._v_instance_count == 0
        bpg = unroll_bodypart(self.root, skipNetwork)
        bpg.unrolled = 1
        log.debug('/BodyPartGraph.unroll (bpg size %d -> size %d)',
                  len(self.bodyparts), len(bpg.bodyparts))
        return bpg

    def sanityCheck(self):
        "See if anything is wrong with this BodyPartGraph"
        log.debug('BodyPartGraph.sanityCheck')
        # check everything we can reach from the root is in our bodypart list
        assert self.root in self.bodyparts
        bps = [self.root]
        assert len([x for x in self.bodyparts if x.isRoot == 1]) == 1
        reachable = bps
        while bps:
            bp = bps[0]
            if bp in reachable:
                # already found
                del bps[0]
            else:
                reachable.append(bp)
                assert self.bodyparts.count(bp) == 1
                #assert bp._v_instance_count >= 0
                for e in bp.edges:
                    assert self.bodyparts.count(e.child) == 1
                    if e.child not in reachable:
                        bps.append(e.child)

        # check every target child is in our bodyparts list
        for i in range(len(self.bodyparts)):
            bp = self.bodyparts[i]
            for e in bp.edges:
                assert self.bodyparts.count(e.child) == 1

        # make sure that everything is connected
        if self.unrolled:
            phen_bpg = self
        else:
            phen_bpg = self.unroll()
        phen_bpg.connectInputNodes(sanitycheck=0)
        # all external inputs should have a single connection, otherwise it
        # should be None
        for bp in phen_bpg.bodyparts:
            for n in bp.network:
                if n in bp.network.inputs:
                    assert n.externalInputs
                    for (sbp, src) in n.externalInputs:
                        assert sbp in phen_bpg.bodyparts
                        if isinstance(src, node.Node):
                            assert src in sbp.network
                            assert src in sbp.network.outputs
                            assert bp != sbp  # no inter-network connections
            # check motor connections
            for i in 0, 1, 2:
                assert bp.motor_input[i]
                (sbp, src, weight) = bp.motor_input[i]
                assert sbp in phen_bpg.bodyparts
                if isinstance(src, node.Node):
                    assert src in sbp.network.outputs
        if not self.unrolled:
            # assert that *genotype* has no motor_inputs
            for bp in self.bodyparts:
                for i in 0, 1, 2:
                    assert not bp.motor_input[i]
        for bp in self.bodyparts:
            if self.unrolled:
                # we only use input maps for the genotype, since phenotype BPs
                # link back to their genotype BPs anyway
                assert not bp.input_map
            else:
                # Make sure all entries in input_map are valid bodyparts and neurons
                for (tsignal, srclist) in bp.input_map.items():
                    assert tsignal in bp.network.inputs or tsignal[:5] == 'MOTOR'
                    for (sbp, ssignal, w) in srclist:
                        assert sbp in self.bodyparts
                        if isinstance(ssignal, node.Node):
                            assert ssignal in sbp.network
                        else:
                            assert ssignal in [
                                'JOINT_0', 'JOINT_1', 'JOINT_2', 'CONTACT'
                            ]
                        assert isinstance(w, float)
        # check src and dst nodes for externalInputs are in respective bp.network
        for bp in phen_bpg.bodyparts:
            sources = phen_bpg.getInputs(bp)
            for (tsignal, (sbp, signal, w)) in sources:
                sbp_i = phen_bpg.bodyparts.index(sbp)
                tbp_i = phen_bpg.bodyparts.index(bp)
                if isinstance(tsignal, node.Node):
                    bp.network.index(tsignal)
                if isinstance(signal, node.Node):
                    assert signal in sbp.network

    def fixup(self):
        """Fix any problems with this BodyPartGraph (ie. invalid connections,
        bad root, etc.) This is called on rolled bpgs after mutation, and
        unrolled after modification to fit simulation constraints (eg.
        MAX_UNROLLED_BODYPARTS).  """
        # remove edges that point to invalid children
        for bp in self.bodyparts:
            edges_to_remove = []
            for e in bp.edges:
                if e.child not in self.bodyparts:
                    #bp.edges.remove(e)
                    edges_to_remove.append(e)
            for e in edges_to_remove:
                bp.edges.remove(e)
        # make sure root exists
        if self.root not in self.bodyparts or len(
            [x for x in self.bodyparts if x.isRoot == 1]) != 1:
            # randomly select the root node
            for b in self.bodyparts:
                b.isRoot = 0
            self.root = random.choice(self.bodyparts)
            self.root.isRoot = 1
        assert len([x for x in self.bodyparts if x.isRoot == 1]) == 1
        # remove input_map entries that are invalid
        for bp in self.bodyparts:
            if bp.input_map:
                # we need to keep a list and erase at the end otherwise we fall into
                # the trap of removing items for a mutable list whilst iterating
                # over it
                for (tneuron, srclist) in bp.input_map.items():
                    if tneuron not in bp.network.inputs:
                        del bp.input_map[tneuron]
                    else:
                        for (sbp, sneuron, w) in srclist[:]:
                            if sbp not in self.bodyparts or sneuron not in sbp.network:
                                srclist.remove((sbp, sneuron, w))
        for bp in self.bodyparts:
            if bp.input_map:
                for (tneuron, srclist) in bp.input_map.items():
                    for (sbp, sneuron, w) in srclist:
                        assert sbp in self.bodyparts

        # check whether input_map entries are still valid
        for bp in self.bodyparts:
            if bp.input_map:
                krm = []
                for k in bp.input_map.keys():
                    if k not in self.bodyparts:
                        krm.append(k)
                    else:
                        # key is valid
                        toremove = []
                        for (sbp, sig, w) in bp.input_map[k]:
                            # check sbp is ok and src is a string or output node
                            if sbp not in self.bodyparts or (
                                    isinstance(sig, node.Node)
                                    and sig not in sbp.network.outputs):
                                toremove.append((sbp, sig, w))
                        for x in toremove:
                            bp.input_map[k].remove(x)
                for k in krm:
                    del bp.input_map[k]

        self.connectInputNodes()
        self.sanityCheck()

    def mutate_delete_edges(self, p):
        "Randomly erase edges in this BodyPartGraph with probability p"
        for bp in self.bodyparts:
            for i in range(len(bp.edges) - 1, -1, -1):
                if random.random() < p:
                    # delete edge
                    log.debug('delete edge')
                    self.mutations += 1
                    del bp.edges[i]
                    self.fixup()
                    self.sanityCheck()

    def mutate_add_edges(self, p):
        "Randomly add edges in this BodyPartGraph with probability p"
        for s_bp in self.bodyparts:
            if random.random() < p:
                #and len(self.bodyparts) < BPG_MAX_EDGES:
                # add edge
                log.debug('add edge')
                self.mutations += 1
                t_bp = random.choice(self.bodyparts)
                e = Edge(t_bp, random.choice([-1, 1]), random.choice([0, 1]))
                s_bp.edges.append(e)
                # we now have new nodes in the unrolled bpg which don't have
                # entries in their genotype bp for their neighbours, so fixup
                self.fixup()
                self.sanityCheck()

    def mutate_delete_nodes(self, p):
        "Randomly delete nodes in this BodyPartGraph with probability p"
        for i in range(len(self.bodyparts) - 1, -1, -1):
            if random.random() < p and len(self.bodyparts) > 1:
                # delete node
                log.debug('delete node')
                self.mutations += 1
                bp_del = self.bodyparts[i]
                # delete all edges pointing to this node
                for bp in self.bodyparts:
                    edges_to_remove = []
                    for e in bp.edges:
                        if e.child == bp_del:
                            edges_to_remove.append(e)
                    for e in edges_to_remove:
                        bp.edges.remove(e)
                self.bodyparts.remove(bp_del)
                if bp_del == self.root:
                    self.root = random.choice(self.bodyparts)
                    self.root.isRoot = 1
                self.fixup()
                self.sanityCheck()

    def mutate_copy_nodes(self, p):
        "Randomly copy nodes in this BodyPartGraph with probability p"
        for i in range(len(self.bodyparts)):
            if random.random() < p and len(self.bodyparts) < BPG_MAX_NODES:
                # copy and mutate node
                log.debug('copy node')
                self.mutations += 1
                c = copy.deepcopy(self.bodyparts[i])
                # we did in fact just copy everything the bp links to ...
                # fixme: correct? yes? efficient? probably not.
                c.edges = PersistentList()
                c.mutate(p)
                self.bodyparts.append(c)

                # random incoming edges
                i = random.randint(1, len(self.bodyparts) / 2)
                for _ in range(i):
                    # add edges
                    e = Edge(c, random.choice([-1, 1]), random.choice([0, 1]))
                    s_bp = random.choice(self.bodyparts)
                    s_bp.edges.append(e)

                # random outgoing edges
                i = random.randint(1, len(self.bodyparts) / 2)
                for _ in range(i):
                    # add edges
                    t_bp = random.choice(self.bodyparts)
                    e = Edge(t_bp, random.choice([-1, 1]),
                             random.choice([0, 1]))
                    c.edges.append(e)
                self.fixup()
                self.sanityCheck()

    def mutate_inputmaps(self, p):
        "Randomly rewire input_maps in each BodyPart with probability p"
        for bp in self.bodyparts:
            for _ in range(len(bp.input_map)):
                if random.random() < p:
                    log.debug('mutate input_map')
                    self.mutations += 1
                    di = random.choice(bp.input_map.keys())
                    if random.random() < 0.5:
                        del bp.input_map[di]
                    else:
                        # mutate weight
                        xp = random.randrange(0, len(bp.input_map[di]))
                        x = list(bp.input_map[di][xp])
                        x[2] = rnd(-7, 7, x[2])
                        bp.input_map[di][xp] = tuple(x)

        self.connectInputNodes()
        self.sanityCheck()

    def mutate(self, p):
        "Mutate the BodyPartGraph nodes, edges, and all parameters."
        log.debug('bpg.mutate(p=%f)', p)

        self.sanityCheck()
        self.mutations = 0
        self.mutate_delete_edges(p)
        self.mutate_add_edges(p)
        self.mutate_delete_nodes(p)
        self.mutate_copy_nodes(p)
        self.mutate_inputmaps(p)

        # FIXME: mutate number of input and output nodes
        # mutate motors and sensors?
        self.sanityCheck()
        for bp in self.bodyparts:
            # mutate individual parameters
            self.mutations += bp.mutate(p)
            # since bp mutate can change the topology of the unrolled graph via
            # recursive_limit, we need to fix up external_input and maybe others
            self.fixup()
            self.sanityCheck()

        self.sanityCheck()

        log.debug('/bpg.mutate')
        return self.mutations
Exemplo n.º 3
0
    def checkTheWorld(self):
        # Test constructors
        u = PersistentList()
        u0 = PersistentList(l0)
        u1 = PersistentList(l1)
        u2 = PersistentList(l2)

        uu = PersistentList(u)
        uu0 = PersistentList(u0)
        uu1 = PersistentList(u1)
        uu2 = PersistentList(u2)

        v = PersistentList(tuple(u))
        class OtherList(object):
            def __init__(self, initlist):
                self.__data = initlist
            def __len__(self):
                return len(self.__data)
            def __getitem__(self, i):
                return self.__data[i]
        v0 = PersistentList(OtherList(u0))
        vv = PersistentList("this is also a sequence")

        # Test __repr__
        eq = self.assertEqual

        eq(str(u0), str(l0), "str(u0) == str(l0)")
        eq(repr(u1), repr(l1), "repr(u1) == repr(l1)")
        eq(repr(u2), repr(l2), "repr(u2) == repr(l2)")

        # Test __cmp__ and __len__

        # Py3: No cmp() or __cmp__ anymore.
        if PY2:
            def mycmp(a, b):
                r = cmp(a, b)
                if r < 0: return -1
                if r > 0: return 1
                return r

            all = [l0, l1, l2, u, u0, u1, u2, uu, uu0, uu1, uu2]
            for a in all:
                for b in all:
                    eq(mycmp(a, b), mycmp(len(a), len(b)),
                          "mycmp(a, b) == mycmp(len(a), len(b))")

        # Test __getitem__

        for i in range(len(u2)):
            eq(u2[i], i, "u2[i] == i")

        # Test __setitem__

        uu2[0] = 0
        uu2[1] = 100
        try:
            uu2[2] = 200
        except IndexError:
            pass
        else:
            self.fail("uu2[2] shouldn't be assignable")

        # Test __delitem__

        del uu2[1]
        del uu2[0]
        try:
            del uu2[0]
        except IndexError:
            pass
        else:
            self.fail("uu2[0] shouldn't be deletable")

        # Test __getslice__

        for i in range(-3, 4):
            eq(u2[:i], l2[:i], "u2[:i] == l2[:i]")
            eq(u2[i:], l2[i:], "u2[i:] == l2[i:]")
            for j in range(-3, 4):
                eq(u2[i:j], l2[i:j], "u2[i:j] == l2[i:j]")

        # Test __setslice__

        for i in range(-3, 4):
            u2[:i] = l2[:i]
            eq(u2, l2, "u2 == l2")
            u2[i:] = l2[i:]
            eq(u2, l2, "u2 == l2")
            for j in range(-3, 4):
                u2[i:j] = l2[i:j]
                eq(u2, l2, "u2 == l2")

        uu2 = u2[:]
        uu2[:0] = [-2, -1]
        eq(uu2, [-2, -1, 0, 1], "uu2 == [-2, -1, 0, 1]")
        uu2[0:] = []
        eq(uu2, [], "uu2 == []")

        # Test __contains__
        for i in u2:
            self.assertTrue(i in u2, "i in u2")
        for i in min(u2)-1, max(u2)+1:
            self.assertTrue(i not in u2, "i not in u2")

        # Test __delslice__

        uu2 = u2[:]
        del uu2[1:2]
        del uu2[0:1]
        eq(uu2, [], "uu2 == []")

        uu2 = u2[:]
        del uu2[1:]
        del uu2[:1]
        eq(uu2, [], "uu2 == []")

        # Test __add__, __radd__, __mul__ and __rmul__

        #self.assertTrue(u1 + [] == [] + u1 == u1, "u1 + [] == [] + u1 == u1")
        self.assertTrue(u1 + [1] == u2, "u1 + [1] == u2")
        #self.assertTrue([-1] + u1 == [-1, 0], "[-1] + u1 == [-1, 0]")
        self.assertTrue(u2 == u2*1 == 1*u2, "u2 == u2*1 == 1*u2")
        self.assertTrue(u2+u2 == u2*2 == 2*u2, "u2+u2 == u2*2 == 2*u2")
        self.assertTrue(u2+u2+u2 == u2*3 == 3*u2, "u2+u2+u2 == u2*3 == 3*u2")

        # Test append

        u = u1[:]
        u.append(1)
        eq(u, u2, "u == u2")

        # Test insert

        u = u2[:]
        u.insert(0, -1)
        eq(u, [-1, 0, 1], "u == [-1, 0, 1]")

        # Test pop

        u = PersistentList([0, -1, 1])
        u.pop()
        eq(u, [0, -1], "u == [0, -1]")
        u.pop(0)
        eq(u, [-1], "u == [-1]")

        # Test remove

        u = u2[:]
        u.remove(1)
        eq(u, u1, "u == u1")

        # Test count
        u = u2*3
        eq(u.count(0), 3, "u.count(0) == 3")
        eq(u.count(1), 3, "u.count(1) == 3")
        eq(u.count(2), 0, "u.count(2) == 0")


        # Test index

        eq(u2.index(0), 0, "u2.index(0) == 0")
        eq(u2.index(1), 1, "u2.index(1) == 1")
        try:
            u2.index(2)
        except ValueError:
            pass
        else:
            self.fail("expected ValueError")

        # Test reverse

        u = u2[:]
        u.reverse()
        eq(u, [1, 0], "u == [1, 0]")
        u.reverse()
        eq(u, u2, "u == u2")

        # Test sort

        u = PersistentList([1, 0])
        u.sort()
        eq(u, u2, "u == u2")

        # Test extend

        u = u1[:]
        u.extend(u2)
        eq(u, u1 + u2, "u == u1 + u2")
Exemplo n.º 4
0
class PSetList(Persistent):
    """PSetList is a persistent set list object that mostly acts just like a normal Python list for PSet objects.
    These lists can be saved in the database just like any other persistent objects. It can optionally be initialized
    with another list of PSet objects and a name. Additionally, it will also have an attribute 'creation_date' and
    a unique uuid attribute 'id'. PSetLists are considered equal if they have the same 'id'.

    Except for the usual list methods like 'extend' and 'append', the PCardList is functional in style, meaning that
    calling any of the other filtering or querying methods return new PCardList objects leaving the original untouched.

    args:
        sets (PSetList, PersistentList[PSet], list[PSet], tuple[PSet]): Initial sets of the list.
        name (str): Name of the set list.
    """

    def __init__(self, sets=None, name=''):
        if isinstance(sets, PSetList):
            self._sets = PersistentList(sets.sets)
        elif isinstance(sets, (PersistentList, list, tuple)):
            self._sets = PersistentList(sets)
        elif not sets:
            self._sets = PersistentList()
        else:
            raise TypeError

        self.name = name
        self.creation_date = datetime.datetime.now()
        self.id = uuid.uuid4()

    def __getitem__(self, item):
        if isinstance(item, int):
            return self._sets.__getitem__(item)
        else:
            return PSetList(self._sets.__getitem__(item))

    def __setitem__(self, key, value):
        self._sets.__setitem__(key, value)

    def __iter__(self):
        return iter(self._sets)

    def __str__(self):
        return str(self._sets)

    def __repr__(self):
        return repr(self._sets)

    def __add__(self, other):
        if isinstance(other, PSetList):
            return PSetList(self.sets + other.sets)
        elif isinstance(other, (PersistentList, list, tuple)):
            return PSetList(self.sets + other)
        elif isinstance(other, PSet):
            new_sets = PersistentList(self.sets)
            new_sets.append(other)
            return PSetList(new_sets)
        else:
            raise TypeError

    def __radd__(self, other):
        if isinstance(other, PSetList):
            return PSetList(self.sets + other.sets)
        elif isinstance(other, (PersistentList, list, tuple)):
            return PSetList(self.sets + other)
        elif isinstance(other, PSet):
            new_sets = PersistentList(self.sets)
            new_sets.append(other)
            return PSetList(new_sets)
        else:
            raise TypeError

    def __iadd__(self, other):
        if isinstance(other, PSetList):
            return PSetList(self.sets + other.sets)
        elif isinstance(other, (PersistentList, list, tuple)):
            return PSetList(self.sets + other)
        elif isinstance(other, PSet):
            new_sets = PersistentList(self.sets)
            new_sets.append(other)
            return PSetList(new_sets)
        else:
            raise TypeError

    def __len__(self):
        return len(self.sets)

    def __contains__(self, pset):
        return self.sets.__contains__(pset)

    def __eq__(self, other):
        if isinstance(other, PSetList):
            return self.id == other.id

    def append(self, pset):
        """Appends the given set object to this list in-place.

        Args:
            pset (PSet): The set object to append.
        """
        self.sets.append(pset)

    def extend(self, psets):
        """Extends the list with a list of set objects in-place.

        Args:
            psets (PSetList, list, tuple, PersistentList): A PSetList, PersistentList, list or a tuple of
                set objects to extend this list with.
        """
        if isinstance(psets, PSetList):
            self.sets.extend(psets.sets)
        elif isinstance(psets, (PersistentList, list, tuple)):
            self.sets.extend(psets)
        else:
            raise TypeError

    def insert(self, index, pset):
        """Inserts a set object to a given index in this list in-place.

        Args:
            pset (PSet): The set object to be inserted in the given index in this list.
            index (int): The index to insert the given set object.
        """
        self._sets.insert(index, pset)

    def index(self, pset):
        """Returns the index where the given set object is located in this list.

        Args:
            pset (PSet): The set object to be searched.
        """
        self._sets.index(pset)

    def clear(self):
        """Clears this list."""
        self._sets.clear()

    def remove(self, pset):
        """Removes a given set from this list in-place.

        Args:
            pset (PSet): A set object to remove from this list.
        """
        self._sets.remove(pset)

    def pop(self, index):
        """Removes a set from a given index from this list in-place.

        Args:
            index (int): An index to remove a set from.
        """
        self._sets.pop(index)

    def count(self, pset):
        """Returns the number of given set objects in this list. Sets are considered same if they have the same code.

        Args:
            pset (pset): A set object to count.

        Returns:
            int: The number of given set objects in this list
        """
        return self._sets.count(pset)

    def sort(self, func):
        """Sorts the sets of this list with a given function in-place. The given function should return some
        attribute of a set object by which this list is sorted.

        Args:
            func: A function to sort this list with.
        """
        self._sets.sort(key=func)

    def filter(self, func):
        """Filters the sets of this list with a given function in-place. The new list contains all the cards
        for which the given function returns True.

        Args:
            func: A function to filter with.
        """
        self._sets.filter(key=func)

    def sorted(self, func):
        """Returns a new list with the sets of this list sorted with a given function. The given function should return
        some attribute of a set object by which this list is sorted.

        Args:
            func: A function to sort this list with.

        Returns:
            PCardList: A new instance of this list sorted.

        """
        return PSetList(sorted(self.sets, key=func))

    def filtered(self, func):
        """Returns a new list filtered with a given function. The new list contains all the sets
        for which the given function returns True.

        Args:
            func: A function to filter with.

        Returns:
            PCardList: A new instance of this list filtered.

        """
        return PSetList(list(filter(func, self.sets)))

    def where(self, invert=False, **kwargs):
        """Returns a new list of sets for which any of the given keyword arguments match partly or completely with the
        attributes of the sets in this list. The arguments should be any set attribute names such as 'name',
        'type' and 'block'. String attributes are case insensitive and it is enough that the argument is a
        substring. For list arguments the order does not matter and it is enough for one of the elements to match.

        The search can also be inverted by setting invert=True so that all the cards NOT matching will be returned.

        Note that searching for Null arguments is not supported.

        Args:
            invert: If True, a list of sets NOT matching the arguments is returned.
            **kwargs: Arguments to match with the attributes of this list's sets.

        Returns:
            bool: A new list of sets for which any of the given keyword arguments match partly or completely.
        """
        del_keys = []

        for (key, val) in kwargs.items():
            if not val:
                msg = 'Ignoring an empty or null value for keyword {}. Null or empty values are not supported.'
                warnings.warn(msg.format(key))
                del_keys.append(key)
            elif len(self.sets) == 0:
                msg = 'Searching an empty list.'
                warnings.warn(msg)
            elif not hasattr(self.sets[0], key):
                msg = 'Ignoring an unrecognized keyword {}. Make sure you are using correct api type and spelling.'
                warnings.warn(msg.format(key))
                del_keys.append(key)

        for key in del_keys:
            del kwargs[key]

        if not invert:
            return PSetList([pset for pset in self if pset.matches_any(**kwargs)])
        else:
            return PSetList([pset for pset in self if not pset.matches_any(**kwargs)])

    def where_exactly(self, invert=False, **kwargs):
        """Returns a new list of sets for which all of the given keyword arguments match completely with the attributes
        of the sets in this list. The arguments should be any set attribute names such as 'name', 'type' and 'block'.
        String attributes are case insensitive and must match exactly. For list arguments the order does not
        matter and and each element must match exactly.

        The search can also be inverted by setting invert=True so that all the cards NOT matching will be returned.

        Note that searching for Null arguments is not supported.

        Args:
            invert: If True, a list of sets NOT matching the arguments is returned.
            **kwargs: Arguments to match with the attributes of this list's cards.

        Returns:
            bool: A new list of sets for which all of the given keyword arguments match completely.
        """
        del_keys = []

        for (key, val) in kwargs.items():
            if not val:
                msg = 'Ignoring an empty or null value for keyword {}. Null or empty values are not supported.'
                warnings.warn(msg.format(key))
                del_keys.append(key)
            elif len(self.sets) == 0:
                msg = 'Searching an empty list.'
                warnings.warn(msg)
            elif not hasattr(self.sets[0], key):
                msg = 'Ignoring an unrecognized keyword {}. Make sure you are using correct api type and spelling.'
                warnings.warn(msg.format(key))
                del_keys.append(key)

        for key in del_keys:
            del kwargs[key]

        if not invert:
            return PSetList([pset for pset in self if pset.matches_all(**kwargs)])
        else:
            return PSetList([pset for pset in self if not pset.matches_all(**kwargs)])

    def pprint(self):
        """Prints out the contents of this list in a nice readable way."""
        print(self.pprint_str())

    def pprint_str(self):
        """Returns a nice readable string of the contents of this list.

        Returns:
            str: a string of the contents of this list in a nice readable format.
        """

        if len(self) == 0:
            if self.name:
                return 'Empty set list "{}" created at {}\n'.format(self.name, str(self.creation_date))
            else:
                return 'Unnamed empty set list created at {}\n'.format(self.creation_date)

        pp_str = ''

        if self.name:
            pp_str += 'Set list "{}" created at {}\n'.format(self.name, str(self.creation_date))
        else:
            pp_str += 'Unnamed set list created at {}\n'.format(self.creation_date)

        longest_name = max(len(pset.name) for pset in self.sets)
        longest_type = max(len(getattr(pset, 'set_type', getattr(pset, 'type', ''))) for pset in self.sets)
        longest_block = max(len(pset.block) if pset.block else 0 for pset in self.sets)
        longest_code = max(len(pset.code) if pset.code else 0 for pset in self.sets)

        pp_str += '-' * (longest_name + longest_type + longest_block + longest_code + 17)
        pp_str += '\n'

        format_str = '{name:{w1}s}   {code:{w2}s}   {block:{w3}s}   {type:{w4}s}   {cards}\n'
        pp_str += format_str.format(name='Set',
                                    w1=longest_name,
                                    code='Code',
                                    w2=longest_code,
                                    block='Block',
                                    w3=longest_block,
                                    type='Type',
                                    w4=longest_type,
                                    cards='Cards')
        pp_str += '-' * (longest_name + longest_type + longest_block + longest_code + 17)
        pp_str += '\n'

        for pset in self.sets:
            format_str = '{name:{w1}s}   {code:{w2}s}   {block:{w3}s}   {type:{w4}s}   {cards}\n'
            pp_str += format_str.format(name=pset.name,
                                        w1=longest_name,
                                        code=pset.code,
                                        w2=longest_code,
                                        block=pset.block if pset.block else '',
                                        w3=longest_block,
                                        type=getattr(pset, 'set_type', getattr(pset, 'type', '')),
                                        w4=longest_type,
                                        cards=len(pset))

        return pp_str

    @property
    def api_type(self):
        try:
            return self.sets[0].api_type
        except IndexError:
            return 'unspecified'

    @property
    def json(self):
        pset_json_dicts = []

        for pset in self.sets:
            json_dict = dict(pset.__dict__)
            del json_dict['_cards']
            del json_dict['_sideboard']
            del json_dict['creation_date']
            del json_dict['id']

            if len(pset) > 0:
                json_dict['cards'] = [card.__dict__ for card in pset.cards]
                pset_json_dicts.append(json_dict)

        return json.dumps({'sets': pset_json_dicts}, sort_keys=True, indent=4)

    @property
    def sets(self):
        return self._sets

    @sets.setter
    def sets(self, sets):
        if isinstance(sets, PSetList):
            self._sets = PersistentList(sets.sets)
        elif isinstance(sets, (list, PersistentList, tuple)):
            self._sets = PersistentList(sets)
        elif not sets:
            self._sets = PersistentList()
        else:
            raise TypeError