コード例 #1
0
    def __init__(self, w, h, state):

        self.state = state
        BaseMap.__init__(self, w, h)
        self.tiles = {}
        self.needs_update = True
        self.edges = {}
        self.edge_tile_map = BorderEdgeTileMap(self)
コード例 #2
0
ファイル: gen-partial.py プロジェクト: Anton495/peano
def bauman():
    proto = ((0, 0), (0, 1), (0, 2), (1, 2), (1, 1), (1, 0), (2, 0), (2, 1),
             (2, 2))
    base_maps = [
        BaseMap(perm=[1, 0], flip=[False, False]),
        BaseMap(perm=[1, 0], flip=[True, False]),
        BaseMap.id_map(dim=2),
        BaseMap(perm=[1, 0], flip=[False, True]),
        BaseMap(perm=[1, 0], flip=[True, True]),
        BaseMap(perm=[0, 1], flip=[False, True]),
        BaseMap(perm=[1, 0], flip=[False, False]),
        BaseMap(perm=[1, 0], flip=[True, False]),
        BaseMap.id_map(dim=2),
    ]
    bauman = FractalCurve(dim=2, div=3, proto=proto, base_maps=base_maps)
    bauman.estimate_ratio_vertex_brkline(ratio_l2, 3)

    #good.estimate_ratio(ratio, rel_tol=0.002, verbose=1)
    bauman = bauman.get_subdivision(1)
    pcurve = bauman.forget()
    pcurve = pcurve.changed(allow_time_rev=True)
    pcurve.estimate_ratio(ratio_l2,
                          lower_bound=32.2,
                          upper_bound=32.1,
                          sat_pack=1000,
                          find_model=True)
コード例 #3
0
ファイル: examples.py プロジェクト: Anton495/peano
def get_hilbert_curve():
    """Example of fractal curve due to D.Hilbert."""
    return FractalCurve(
        proto=[(0, 0), (0, 1), (1, 1), (1, 0)],
        base_maps=[
            BaseMap([1, 0], [False, False]),  # (x,y)->(y,x)
            BaseMap.id_map(2),                # (x,y)->(x,y)
            BaseMap.id_map(2),                # (x,y)->(x,y)
            BaseMap([1, 0], [True, True]),    # (x,y)->(1-y,1-x)
        ],
    )
コード例 #4
0
ファイル: examples.py プロジェクト: Anton495/peano
def get_rev3_curve():
    """Curve with time reversal at last cube."""
    return FractalCurve(
        proto=[(0, 0), (0, 1), (1, 1), (1, 0)],
        base_maps=[
            BaseMap([1, 0], [False, False]),                # (x,y)->(y,x)
            BaseMap.id_map(2),                              # (x,y)->(x,y)
            BaseMap.id_map(2),                              # (x,y)->(x,y)
            BaseMap([1, 0], [True, False], time_rev=True),  # (x,y)->(1-y,x), t->1-t
        ],
    )
コード例 #5
0
ファイル: examples.py プロジェクト: Anton495/peano
def get_peano_curve():
    """Example of fractal curve due to G.Peano."""
    id_map = BaseMap.id_map(2)
    x_map = BaseMap([0, 1], [True, False])  # (x,y)->(1-x,y)
    y_map = BaseMap([0, 1], [False, True])  # (x,y)->(x,1-y)
    xy_map = BaseMap([0, 1], [True, True])  # (x,y)->(1-x,1-y)
    return FractalCurve(
        proto=[(0, 0), (0, 1), (0, 2), (1, 2), (1, 1), (1, 0), (2, 0), (2, 1), (2, 2)],
        base_maps=[
            id_map, x_map, id_map,
            y_map, xy_map, y_map,
            id_map, x_map, id_map,
        ],
    )
コード例 #6
0
    def get_edge_touch(self, edge):
        """Find moment of first edge touch.
        Edge is a tuple of {0,1,None} defining a set
        {(x_0,...,x_{d-1}): x_i==0 if e[i]==0, x_i==1 if e[i]==1, or arbitrary x[i] if e[i] is None.
        E.g., tuples (0,0,0) or (0,1,1) define vertices
        """
        curve = self
        cur_map = BaseMap.id_map(self.dim)  # curve = cur_map * self
        cnums = []
        index = {}
        while True:
            cnum = curve._get_edge_cnum(edge)
            bm = curve.base_maps[cnum]
            cnums.append(cnum)

            index[cur_map] = len(cnums) - 1

            cur_map = bm * cur_map
            if cur_map in index:
                period_start = index[cur_map]
                break
            curve = curve.apply_base_map(bm)

        return self._get_time_limit(cnums[0:period_start],
                                    cnums[period_start:])
コード例 #7
0
    def divide(self):
        curve = self.curve
        dim, G = curve.dim, curve.genus

        # определим ориентацию предпоследнего кусочка
        # в последнем кубе ориентация не задана!
        prev_map = BaseMap.id_map(dim)
        for cnum in self.pos.cnums[:-1]:
            cnum = prev_map.apply_cnum(G, cnum)
            prev_map = prev_map * curve.base_maps[
                cnum]  # именно в таком порядке!

        prev_curve = curve.apply_base_map(prev_map)

        active_cnum = self.pos.cnums[-1]  # кубик, где всё происходит
        spec_cnum = prev_map.apply_cnum(G, active_cnum)

        # делим
        for bm in prev_curve.gen_allowed_maps(active_cnum):

            # сопряжение, как в apply:
            # для prev_curve.base_maps[active_cnum] = bm =>  orig_curve.base_maps[spec_cnum] = ...
            new_map = prev_map.inverse() * bm * prev_map
            specified_curve = curve.specify(spec_cnum, new_map)

            last_curve = prev_curve.apply_base_map(bm)
            for cnum, cube in enumerate(last_curve.proto):
                new_pos = self.pos.specify(cnum, cube)
                new_piece = CurvePiece(specified_curve, new_pos)
                yield new_piece
コード例 #8
0
ファイル: bm.py プロジェクト: Anton495/peano
 def setUp(self):
     self.pm_examples = [
         PieceMap(
             base_map=BaseMap([3,2,1,0],[True,False,False,False]),
             shift=(-1,-2,-3,-4),
             scale=3,
             time_shift=-10,
             time_scale=1,
         ),
         PieceMap(
             base_map=BaseMap([4,0,1,3,2],[True,False,False,True,True]),
             shift=(-Fraction(1, 2), Fraction(1, 2), Fraction(1, 3), Fraction(1, 5), Fraction(1, 7)),
             scale=Fraction(1, 11),
             time_shift=0,
             time_scale=1,
         ),
     ]
コード例 #9
0
    def load_map(cls, filename):
        asset_path = ''.join((os.path.dirname(__file__),
                              '\\..\\..\\assets\\maps\\', filename, '.txt'))
        f = open(asset_path, 'r')
        map_lines = []
        for line in f:
            if line.strip() == '':
                continue
            map_lines.append(list(line.rstrip()))
        f.close()

        h = len(map_lines)
        w = len(map_lines[0])
        map = [[map_lines[y][x] for y in range(h)] for x in range(w)]
        new = BaseMap(w, h)
        new.set_map(map)

        # temp - modify block map pre real map generator algo
        cls.add_blocks(new)

        return new
コード例 #10
0
ファイル: examples.py プロジェクト: Anton495/peano
def get_peano5_curve():
    id_map = BaseMap.id_map(2)
    x_map = BaseMap([0, 1], [True, False])  # (x,y)->(1-x,y)
    y_map = BaseMap([0, 1], [False, True])  # (x,y)->(x,1-y)
    xy_map = BaseMap([0, 1], [True, True])  # (x,y)->(1-x,1-y)
    return FractalCurve(
        dim=2, div=5,
        proto=[
            (0, 0), (0, 1), (0, 2), (0, 3), (0, 4),
            (1, 4), (1, 3), (1, 2), (1, 1), (1, 0),
            (2, 0), (2, 1), (2, 2), (2, 3), (2, 4),
            (3, 4), (3, 3), (3, 2), (3, 1), (3, 0),
            (4, 0), (4, 1), (4, 2), (4, 3), (4, 4),
        ],
        base_maps=[
            id_map, x_map, id_map, x_map, id_map,
            y_map, xy_map, y_map, xy_map, y_map,
            id_map, x_map, id_map, x_map, id_map,
            y_map, xy_map, y_map, xy_map, y_map,
            id_map, x_map, id_map, x_map, id_map,
        ],
    )
コード例 #11
0
ファイル: examples.py プロジェクト: Anton495/peano
def get_scepin_bauman_curve():
    proto = (
        (0, 0), (0, 1), (0, 2),
        (1, 2), (1, 1), (1, 0),
        (2, 0), (2, 1), (2, 2),
    )
    base_maps = [
        BaseMap.id_map(dim=2),
        BaseMap([1,0],[True,False]),  # rot(90)
        BaseMap([1,0],[False,False]),  # (x,y)->(y,x)

        BaseMap([0,1],[False,True]),  # (x,y)->(x,1-y)
        BaseMap([1,0],[True,True]),  # (x,y)->(1-y,1-x)
        BaseMap([1,0],[False,True]),  # rot(-90)

        BaseMap.id_map(dim=2),
        BaseMap(perm=[1,0],flip=[True,False]),  # rot(90)
        BaseMap(perm=[1,0],flip=[False,False]),  # (x,y)->(y,x)
    ]
    return FractalCurve(dim=2, div=3, proto=proto, base_maps=base_maps)
コード例 #12
0
ファイル: examples.py プロジェクト: Anton495/peano
def get_haverkort_curve_A26():
    """3-D curve with time reversal."""
    proto = [(0,0,0), (0,0,1), (0,1,1), (0,1,0), (1,1,0), (1,1,1), (1,0,1), (1,0,0)]
    base_maps = [
        BaseMap([2, 1, 0], [False, False, False]),
        BaseMap([2, 0, 1], [False, False, False]),
        BaseMap([2, 0, 1], [False, True, False], time_rev=True),
        BaseMap([0, 2, 1], [False, True, True]),
        BaseMap([0, 2, 1], [True, True, True], time_rev=True),
        BaseMap([2, 0, 1], [True, True, False]),
        BaseMap([2, 0, 1], [True, False, False], time_rev=True),
        BaseMap([1, 2, 0], [True, False, False], time_rev=True),
    ]
    return FractalCurve(
        proto=proto,
        base_maps=base_maps,
    )
コード例 #13
0
    def _get_cubes(self, cnum):
        # находим последовательность вложенных кубов, которая получится, если в каждой фракции брать куб с номером cnum
        # возвращает пару (непериодическая часть, периодическая часть)
        cur_map = BaseMap.id_map(self.dim)  # current curve = cur_map * self
        cubes = []
        index = {}

        while True:
            cur_curve = self.apply_base_map(cur_map)
            cube = cur_curve.proto[cnum]

            cubes.append(cube)
            index[cur_map] = len(cubes) - 1

            cur_map = cur_curve.base_maps[cnum] * cur_map
            if cur_map in index:
                idx = index[cur_map]
                return cubes[0:idx], cubes[idx:]
コード例 #14
0
ファイル: utils.py プロジェクト: Anton495/peano
def bmstr2base_map(bmstr):
    if '1-t' in bmstr:
        time_rev = True
    else:
        time_rev = False
    match = re.match('\(x,y\)->\((.*),(.*)\)', bmstr)
    if not match:
        print('@@@', bmstr)

    g1, g2 = match.groups()
    if 'x' in g1:
        perm = [0, 1]
    else:
        perm = [1, 0]
    flip = [False] * 2
    flip[0] = ('1-' in g1)
    flip[1] = ('1-' in g2)

    return BaseMap(perm, flip, time_rev)
コード例 #15
0
ファイル: utils.py プロジェクト: Anton495/peano
def basis2base_map(basis):

    dim = len(basis) - 1 if basis[-1] in ['0', '1'] else len(basis)

    letters = 'ijklmn'
    assert dim <= 6

    l2i = {l: i for i, l in enumerate(letters)}
    perm = [None] * dim
    flip = [None] * dim
    time_rev = True if basis[-1] == '1' else False

    basis = basis[:-1] if basis[-1] in ['0', '1'] else basis

    for k, l in enumerate(basis):
        lk = l.lower()
        perm[k] = l2i[lk]
        flip[k] = (l != lk)

    return BaseMap(perm, flip, time_rev)
コード例 #16
0
    def __init__(self, w, h, river):

        self.river = river
        BaseMap.__init__(self, w, h)
コード例 #17
0
    return Variable(mask)

# -----------------------------------------------------------------------------
encoder = GenericMap(
    [1,1], [r_dim], siamese_input=False,
    num_hidden_layers=2, hidden_dim=40,
    siamese_output=False, act='relu',
    deterministic=True,
    use_bn=use_bn
)
encoder_optim = Adam(encoder.parameters(), lr=encoder_lr)

base_map = BaseMap(
    z_dim, [1], [1], siamese_input=False,
    num_hidden_layers=2, hidden_dim=40,
    siamese_output=False, act='relu',
    deterministic=True,
    use_bn=use_bn
)
base_map_optim = Adam(base_map.parameters(), lr=base_map_lr)

r_to_z_map = GenericMap(
    [r_dim], [z_dim], siamese_input=False,
    num_hidden_layers=2, hidden_dim=40,
    siamese_output=False, act='relu',
    deterministic=False,
    use_bn=use_bn
)
r_to_z_map_optim = Adam(r_to_z_map.parameters(), lr=r_to_z_map_lr)

neural_process = NeuralProcessV1(
コード例 #18
0
            out = lin_out + z_out
            out = F.relu(out)

        lin_out = self.lin_mod_list[-1](out)
        z_out = torch.matmul(out.view(out.size(0), 1, out.size(-1)), z_Ws[-1])
        z_out = z_out.view(-1, z_out.size(-1))
        out = lin_out + z_out

        return out

        # for lin in self.lin_mod_list():
        #     out = torch.matmul(out, )


base_map = BaseMap()

encoder_optim = Adam(encoder.parameters(), lr=encoder_lr)
r_to_z_map_optim = Adam(r_to_z_map.parameters(), lr=r_to_z_map_lr)
base_map_optim = Adam(base_map.parameters(), lr=base_map_lr)
# -----------------------------------------------------------------------------
test_elbos = defaultdict(list)
test_log_likelihoods = defaultdict(list)
for iter_num in range(max_iters):
    task_batch_idxs = choice(len(all_tasks),
                             size=num_tasks_per_batch,
                             replace=replace)
    num_samples_per_task = array(
        [num_per_task_high for _ in range(num_tasks_per_batch)])
    max_num = num_per_task_high
コード例 #19
0
    def __init__(self, w, h, moisture_map):

        BaseMap.__init__(self, w, h)
        self.moisture_map = moisture_map
コード例 #20
0
    def estimate_ratio(self,
                       ratio_func,
                       junctions=None,
                       upper_bound=None,
                       max_iter=None,
                       rel_tol=None,
                       find_argmax=False,
                       verbose=0):
        """Estimate ratio of a curve for given junctions.
        Params:
            ratio_func      generic function for ratio; args: d, delta_v, delta_t
                                we assume it is d-homogeneous
            junctions       list of junctions (None for all junctions, [None] for self reduced ratio)
            upper_bound     apriori upper bound for ratio; we stop if higher ratio is found
            max_iter        subj
            rel_tol         relative error tolerance
            verbose         print every iteration
            find_argmax     subj
        Returns dict with keys:
            lower_bound     bounds for ratio
            upper_bound
            argmax          dict with keys {v1,t1,v2,t2,junc}, describing ratio argmax (if options find_argmax is set)
            stats           counter with some statistics
        """

        # Алгоритм работы:
        # сравниваем пары фракций:
        # 1) оцениваем отношение снизу (через вершины, например)
        # если отношение больше текущей верхней оценки - выходим
        # 2) оцениваем отношение сверху (по прототипу)
        # - если меньше текущей нижней оценки - останавливаемся, не подразбиваем
        # - иначе, подразбиваем обе фракции

        if any(bm.time_rev for bm in self.base_maps):
            raise Exception("Not implemented for curves with time reversal!")
        if max_iter is None and rel_tol is None:
            raise Exception("Define max_iter or rel_tol!")

        if junctions is None:
            junctions = set(self.gen_junctions())
            junctions.add(None)

        d = self.dim
        N = self.div
        G = self.genus

        # тут можно использовать другие ломаные
        self_brkline = self.get_vertex_brkline()

        # приводим ломаную к целым числам; множители lcm_x, lcm_t применим лишь в самом конце вычислений
        denoms = []
        for v, t in self_brkline:
            denoms.append(t.denominator)
            denoms += [x.denominator for x in v]
        lcm_x = get_lcm(denoms)
        lcm_t = lcm_x**d
        int_brkline = []
        for v, t in self_brkline:
            new_t = int(t * lcm_t)
            new_v = tuple(int(x * lcm_x) for x in v)
            int_brkline.append((new_v, new_t))

        argmax = None
        stats = Counter()
        curr_lower_bound = FastFraction(0, 1)
        curr_upper_bound = None

        rich_pairs = []  # пары кривых с доп. информацией
        entry_count = 0
        for junc in junctions:
            if junc is None:
                delta_x = (0, ) * d
                delta_t = 0
                junc_base_map = BaseMap.id_map(self.dim)
            else:
                delta_x, junc_base_map = junc.delta_x, junc.base_map
                delta_t = 1

            start_pair = self.CurveBalancedPair(
                delta_x=delta_x,
                delta_t=delta_t,
                base_map=junc_base_map,
                compare=0,
            )
            if find_argmax:
                start_pair.orig_map = PieceMap.id_map(d)

            # дробим каждую из частей (дробление только одной не уменьшает кол-во итераций)
            junc_pairs = []
            for pair in self.divide_pair(start_pair):
                for sub_pair in self.divide_pair(pair):
                    if sub_pair.delta_t <= 1:
                        continue
                    junc_pairs.append(sub_pair)

            for pair in junc_pairs:
                # оцениваем сверху отношение для пары, чтобы приоритезировать пары с высоким отношением
                up_ratio = pair.get_upper_bound(N, ratio_func)

                rich_pair = {
                    'pair': pair,
                    'upper_bound': up_ratio,
                    'max_subdivision': 1
                }
                if find_argmax:
                    rich_pair['junc'] = junc
                entry_count += 1
                priority = -up_ratio
                heappush(rich_pairs, (priority, entry_count, rich_pair))

        seen_pairs = set()
        while rich_pairs:
            stats['iter'] += 1
            if max_iter is not None and stats['iter'] >= max_iter:
                break

            rich_pair = heappop(rich_pairs)[-1]
            pair = rich_pair['pair']
            delta_x = pair.delta_x
            delta_t = pair.delta_t
            base_map = pair.base_map

            # учитываем, что одна из фракций "раздута"
            if pair.compare == 0:
                mx1, mx2 = 1, 1
            elif pair.compare == 1:
                mx1, mx2 = N, 1
            else:
                mx1, mx2 = 1, N
            mt1, mt2 = mx1**d, mx2**d

            pair_position = (delta_x, delta_t, base_map, pair.compare)
            if pair_position in seen_pairs:
                # пара уже встречалась, новых оценок нам не даст
                stats['seen_pair'] += 1
                continue
            else:
                seen_pairs.add(pair_position)

            stats['max_subdivision'] = max(stats['max_subdivision'],
                                           rich_pair['max_subdivision'])

            if verbose:
                print('iter: {}; max_subdivision: {}; ratio: {} <= X <= {}'.
                      format(
                          stats['iter'],
                          stats['max_subdivision'],
                          float(curr_lower_bound),
                          (float(curr_upper_bound)
                           if curr_upper_bound is not None else '?'),
                      ))

            # могла обновиться нижняя оценка, и пара больше не актуальна!
            up_ratio = rich_pair['upper_bound']
            if up_ratio <= curr_lower_bound:
                stats['stop_early'] += 1
                continue

            #
            # Обновим верхнюю границу (curr_upper_bound)
            #

            # здесь мы используем, что rich_pairs это heap по priority = (-upper_bound)
            curr_upper_bound = max(
                up_ratio,
                rich_pairs[0][-1]['upper_bound']) if rich_pairs else up_ratio
            if up_ratio <= curr_lower_bound:
                # нам эта пара больше не интересна!
                stats['stop_divide'] += 1
                continue

            #
            # Обновим нижнюю оценку (curr_lower_bound)
            #

            # поскольку приводили к целым числам, нужно всё умножить на lcm_x (соотв., lcm_t)
            if mx1 == 1:
                brkline1 = int_brkline
            else:
                brkline1 = []
                for v, t in int_brkline:
                    scaled_v = tuple(v[j] * mx1 for j in range(d))
                    scaled_t = t * mt1
                    brkline1.append((scaled_v, scaled_t))

            brkline2 = []
            for v, t in int_brkline:
                # повернуть + масштабировать
                rot_v = base_map.apply_x2(v, lcm_x)
                if mx2 == 1:
                    scaled_v = rot_v
                    scaled_t = t
                else:
                    scaled_v = tuple(rot_v[j] * mx2 for j in range(d))
                    scaled_t = t * mt2
                brkline2.append((scaled_v, scaled_t))

            # так как в ломаных уже "сидят" lcm-ы, умножаем только дельты
            scaled_delta_x = tuple(dx * lcm_x for dx in delta_x)
            scaled_delta_t = delta_t * lcm_t
            for v2, t2 in brkline2:
                v2_shifted = tuple(v2[j] + scaled_delta_x[j] for j in range(d))
                t2_shifted = t2 + scaled_delta_t
                for v1, t1 in brkline1:
                    dv = tuple(v2_shifted[j] - v1[j] for j in range(d))
                    dt = t2_shifted - t1
                    ratio = FastFraction(*ratio_func(d, dv, dt))
                    if ratio > curr_lower_bound:
                        curr_lower_bound = ratio
                        if find_argmax:
                            inv = pair.orig_map.inverse()
                            # нужно вернуться обратно
                            # сначала уберём lcm-ы
                            v1_frac = tuple(
                                Fraction(v1[j], lcm_x) for j in range(d))
                            v2_frac = tuple(
                                Fraction(v2_shifted[j], lcm_x)
                                for j in range(d))

                            t1_frac = Fraction(t1, lcm_t)
                            t2_frac = Fraction(t2_shifted, lcm_t)

                            # теперь обратное преобразование
                            (orig_v1, orig_t1) = inv.apply(v1_frac, t1_frac)
                            (orig_v2, orig_t2) = inv.apply(v2_frac, t2_frac)

                            # v2, t2 считаем относительно второй фракции
                            junc = rich_pair['junc']
                            if junc is not None:
                                orig_v2 = tuple(orig_v2[j] - junc.delta_x[j]
                                                for j in range(d))
                                orig_t2 -= 1
                            argmax = {
                                'v1': orig_v1,
                                't1': orig_t1,
                                'v2': orig_v2,
                                't2': orig_t2,
                                'junc': junc,
                            }

            if upper_bound is not None and curr_lower_bound > upper_bound:
                break

            if rel_tol is not None and float(curr_upper_bound) <= (
                    1 + rel_tol) * float(curr_lower_bound):
                break

            for sub_pair in self.divide_pair(pair):
                max_subdivision = rich_pair['max_subdivision']
                if pair.compare == 0:
                    max_subdivision += 1  # если фракции были равны, номер подразбиения увеличился

                up_ratio = sub_pair.get_upper_bound(N, ratio_func)
                if up_ratio <= curr_lower_bound:
                    # нам эта пара больше не интересна!
                    continue

                new_rich_pair = {
                    'pair': sub_pair,
                    'upper_bound': up_ratio,
                    'max_subdivision': max_subdivision
                }
                if 'junc' in rich_pair:
                    new_rich_pair['junc'] = rich_pair['junc']
                entry_count += 1
                heappush(rich_pairs, (-up_ratio, entry_count, new_rich_pair))

        return {
            'lower_bound': curr_lower_bound,
            'upper_bound': curr_upper_bound,
            'argmax': argmax,
            'stats': stats,
        }
コード例 #21
0
ファイル: examples.py プロジェクト: Anton495/peano
def get_discontinuous_curve():
    return FractalCurve(
        proto=[(1, 1), (0, 1), (0, 0), (1, 0), (2, 0), (2, 1), (2, 2), (1, 2), (0, 2)],
        base_maps=[BaseMap.id_map(2)] * 9,
    )
コード例 #22
0
ファイル: bm.py プロジェクト: Anton495/peano
 def test_mul(self):
     bm1 = BaseMap([0,1],[True,False])  # (1-x,y)
     bm2 = BaseMap([1,0],[False,False])  # (y,x)
     self.assertEqual(bm1 * bm2, BaseMap([1,0],[True,False]))
     self.assertEqual(bm2 * bm1, BaseMap([1,0],[False,True]))
コード例 #23
0
ファイル: bm.py プロジェクト: Anton495/peano
 def test_inv(self):
     bm = BaseMap(perm=[3,2,1,0], flip=[True,True,True,True])
     self.assertEqual(bm * bm.inverse(), BaseMap.id_map(dim=4))
     self.assertEqual(bm, bm.reverse_time().reverse_time())