def make_approximation_f(name, approx_perc, approx_abs, approx_abs_S, max_value, max_value_S, ndp): F = ndp.get_ftype(name) ndp_before = get_approx_dp(F, name, approx_perc, approx_abs, approx_abs_S, max_value, max_value_S) name2ndp = {NAME_ORIGINAL: ndp, NAME_APPROX: ndp_before} fnames = ndp.get_fnames() rnames = ndp.get_rnames() connections = [] connections.append(Connection(NAME_APPROX, name, NAME_ORIGINAL, name)) for fn in fnames: F = ndp.get_ftype(fn) fn_ndp = dpwrap(Identity(F), fn, fn) fn_name = get_name_for_fun_node(fn) name2ndp[fn_name] = fn_ndp if fn == name: connections.append(Connection(fn_name, fn, NAME_APPROX, fn)) else: connections.append(Connection(fn_name, fn, NAME_ORIGINAL, fn)) for rn in rnames: R = ndp.get_rtype(rn) rn_ndp = dpwrap(Identity(R), rn, rn) rn_name = get_name_for_res_node(rn) name2ndp[rn_name] = rn_ndp connections.append(Connection(NAME_ORIGINAL, rn, rn_name, rn)) return CompositeNamedDP.from_parts(name2ndp, connections, fnames, rnames)
def check_compose(): from mocdp.example_battery.dp_bat import BatteryDP from mocdp.example_battery.dp_bat2 import Mobility actuation = dpwrap(Mobility(), 'weight', 'actuation_power') check_ftype(actuation, 'weight', R_Weight_g) check_rtype(actuation, 'actuation_power', R_Power) battery = dpwrap(BatteryDP(energy_density=100.0), 'capacity', 'weight') check_ftype(battery, 'capacity', R_Energy) check_rtype(battery, 'weight', R_Weight_g) times = dpwrap(Product(R_Time, R_Power, R_Energy), ['mission_time', 'power'], 'energy') check_ftype(times, 'mission_time', R_Time) check_ftype(times, 'power', R_Power) check_rtype(times, 'energy', R_Energy) c = Connection('actuation', 'actuation_power', 'times', 'power') x = dpconnect(dict(actuation=actuation, times=times), [c]) print('WE have obtained x') print('x = %s' % x) print('x fun: %s' % x.get_dp().get_fun_space()) print('x res: %s' % x.get_dp().get_res_space()) # "battery.capacity >= x.energy" c = Connection('x', 'energy', 'battery', 'capacity') _y = dpconnect(dict(battery=battery, x=x), [c])
def check_compose2_generic(): actuation = dpwrap(Mobility(), 'weight', 'actuation_power') battery = dpwrap((BatteryDP(energy_density=100.0)), 'capacity', 'battery_weight') times = dpwrap((Product(R_Time, R_Power, R_Energy)), ['mission_time', 'power'], 'energy') c1 = Connection('actuation', 'actuation_power', 'times', 'power') c2 = Connection('times', 'energy', 'battery', 'capacity') c3 = Connection('battery', 'battery_weight', 'actuation', 'weight') y = dpgraph(dict(actuation=actuation, times=times, battery=battery), [c1, c2, c3], split=[]) print y.desc() assert y.get_fnames() == ['mission_time'], y.get_fnames() assert y.get_rnames() == [], y.get_rnames() check_ftype(y, 'mission_time', R_Time) dp = y.get_dp() funsp = dp.get_fun_space() ressp = dp.get_res_space() assert funsp == R_Time, funsp assert ressp == PosetProduct(()), ressp
def cndp_remove_one_child(cndp, to_remove): """ Removes a child from NDP. Assumes ndp connected. Dangling arrows are substituted with top/bottom. """ print('removing %r' % to_remove) cndp.check_fully_connected() name2ndp = cndp.get_name2ndp() assert to_remove in name2ndp connections = cndp.get_connections() fnames = cndp.get_fnames() rnames = cndp.get_rnames() name2ndp_i = dict((k, v) for k, v in name2ndp.items() if k != to_remove) filter_c = lambda c: not c.involves_any_of_these_nodes([to_remove]) connections_i = filter(filter_c, connections) ndp2 = CompositeNamedDP.from_parts(name2ndp=name2ndp_i, connections=connections_i, fnames=fnames, rnames=rnames) unconnected_fun, unconnected_res = get_missing_connections(ndp2.context) for r in [CResource(*_) for _ in unconnected_res]: R = ndp2.context.get_rtype(r) values = R.get_maximal_elements() dp = LimitMaximals(R, values) new_ndp = dpwrap(dp, 'limit', []) name = ndp2.context.new_name('_limit_r') ndp2.context.add_ndp(name, new_ndp) c = Connection(dp2=name, s2='limit', dp1=r.dp, s1=r.s) ndp2.context.add_connection(c) for f in [CFunction(*_) for _ in unconnected_fun]: F = ndp2.context.get_ftype(f) values = F.get_minimal_elements() dp = ConstantMinimals(F, values) new_ndp = dpwrap(dp, [], 'limit') name = ndp2.context.new_name('_limit_f') ndp2.context.add_ndp(name, new_ndp) c = Connection(dp1=name, s1='limit', dp2=f.dp, s2=f.s) ndp2.context.add_connection(c) ndp2.check_fully_connected() return ndp2
def get_approx_dp(S, name, approx_perc, approx_abs, approx_abs_S, max_value, max_value_S): from mcdp_posets.types_universe import express_value_in_isomorphic_space approx_abs_ = express_value_in_isomorphic_space(S1=approx_abs_S, s1=approx_abs, S2=S) max_value_ = express_value_in_isomorphic_space(S1=max_value_S, s1=max_value, S2=S) if approx_perc > 0: # pragma: no cover raise_desc(DPNotImplementedError, 'Approx_perc not implemented') dps = [] if max_value > 0: dp_max = FuncNotMoreThan(S, max_value_) dps.append(dp_max) if approx_abs_ > 0: dps.append(makeLinearCeilDP(S, approx_abs_)) dp = wrap_series(S, dps) ndp = dpwrap(dp, name, name) return ndp
def get_providers(self, R, lb): """ Returns a list of (name, fname) that implements R, with lb being a lower bound. """ options = [] # check that if it is a bottom if len(lb.minimals) == 1: try: bot = R.get_bottom() except NotBounded: pass else: if R.equal(bot, list(lb.minimals)[0]): # we can provide an "unused" box dp = Limit(R, bot) ndp = dpwrap(dp, 'limit', []) def sanitize(s): import re s = re.sub('[^0-9a-zA-Z]+', '_', s) return s newname = sanitize('limit_%s' % R) options.append((newname, 'limit')) self.additional[newname] = ndp type_options = self.get_providers_for_type(R) for id_ndp, fname in type_options: if self.does_provider_provide(id_ndp, fname, R, lb): options.append((id_ndp, fname)) print('Options for %s >= %s: %r' % (R, lb, options)) return options
def wrap_change_name_resource(ndp, rn, rn2): from mocdp.comp.wrap import dpwrap R = ndp.get_rtype(rn) tmpname = '__tmp_%s' % rn second = dpwrap(Identity(R), tmpname, rn2) from mocdp.comp.connection import connect2 connections = set([Connection('-', rn, '-', tmpname)]) return connect2(ndp, second, connections, split=[])
def reorder_resources(ndp, rnames): R = ndp.get_rtypes(rnames) ndp2 = dpwrap(Identity(R), rnames, rnames) connections = [Connection('*', rn, '*', rn) for rn in rnames] return connect2(res, ndp2, set(connections), split=[], repeated_ok=True)
def wrap_change_name_function(ndp, fn, fn2): from mocdp.comp.wrap import dpwrap F = ndp.get_ftype(fn) tmpname = '__tmp_%s' % fn first = dpwrap(Identity(F), fn2, tmpname) from mocdp.comp.connection import connect2 connections = set([Connection('-', tmpname, '-', fn)]) return connect2(first, ndp, connections, split=[])
def reorder_functions(ndp, rnames): F = ndp.get_ftypes(rnames) ndp0 = dpwrap(Identity(F), rnames, rnames) connections = [Connection('*', fn, '*', fn) for fn in rnames] return connect2(ndp0, res, set(connections), split=[], repeated_ok=True)
def ignore_some(ndp, ignore_fnames, ignore_rnames): """ Ignores some functionalities or resources """ fnames0 = ndp.get_fnames() rnames0 = ndp.get_rnames() for fname in ignore_fnames: check_isinstance(fname, str) if not fname in fnames0: msg = 'Could not find functionality %r in %r.' % (fname, fnames0) raise_desc(ValueError, msg, fname=fname, fnames=fnames0) for rname in ignore_rnames: check_isinstance(rname, str) if not rname in rnames0: msg = 'Could not find resource %r in %r.' % (rname, rnames0) raise_desc(ValueError, msg, rname=rname, rnames=rnames0) c = Context() orig = '_orig' c.add_ndp(orig, ndp) for fname in ndp.get_fnames(): F = ndp.get_ftype(fname) if fname in ignore_fnames: dp = Constant(F, F.get_bottom()) n = '_const_f_%s' % fname c.add_ndp(n, dpwrap(dp, [], fname)) else: n = c.add_ndp_fun_node(fname, F) con = Connection(n, fname, orig, fname) c.add_connection(con) for rname in ndp.get_rnames(): R = ndp.get_rtype(rname) if rname in ignore_rnames: dp = LimitMaximals(R, R.get_maximal_elements()) n = '_const_r_%s' % rname c.add_ndp(n, dpwrap(dp, rname, [])) else: n = c.add_ndp_res_node(rname, R) con = Connection(orig, rname, n, rname) c.add_connection(con) return CompositeNamedDP.from_context(c)
def add_ndp_res_node(self, rname, R): """ returns the name of the node """ if '-' in rname: raise ValueError(rname) ndp = dpwrap(ResourceNode(R, rname), rname, rname) name = get_name_for_res_node(rname) # self.info('Adding new resource %r as %r ' % (str(name), rname)) self.add_ndp(name, ndp) self.rnames.append(rname) return name
def add_ndp_fun_node(self, fname, F): """ Returns the name of the node (something like _fun_****) """ if '-' in fname: raise ValueError(fname) ndp = dpwrap(FunctionNode(F, fname), fname, fname) name = get_name_for_fun_node(fname) # print('Adding new function %r as %r.' % (str(name), fname)) self.add_ndp(name, ndp) self.fnames.append(fname) return name
def check_compose2_fail(): """ Fails because there is a recursive constraint """ actuation = dpwrap((Mobility()), 'weight', 'actuation_power') battery = dpwrap((BatteryDP(energy_density=100.0)), 'capacity', 'weight') times = dpwrap((Product(R_Time, R_Power, R_Energy)), ['mission_time', 'power'], 'energy') try: c1 = Connection('actuation', 'actuation_power', 'times', 'power') c2 = Connection('times', 'energy', 'battery', 'capacity') c3 = Connection('battery', 'weight', 'actuation', 'weight') dpconnect(dict(actuation=actuation, times=times, battery=battery), [c1, c2, c3]) except TheresALoop: pass
def abstract(self): n0 = self.ndp.abstract() dp0 = n0.get_dp() dp = LabelerDP(dp0, self.recursive_name) fnames = self.get_fnames() rnames = self.get_rnames() if len(fnames) == 1: fnames = fnames[0] if len(rnames) == 1: rnames = rnames[0] return dpwrap(dp, fnames, rnames)
def test_exc_connect_resources_to_outside(): F1 = parse_poset('s') R1 = parse_poset('s') dp = Template(F1, R1) ndp = dpwrap(dp, 'f1', 'r1') ndp_name = 'name' name2ndp = {ndp_name: ndp} connections = [] rnames = ['r1', 'notpresent'] assert_raises(ValueError, connect_resources_to_outside, name2ndp, connections, ndp_name, rnames)
def check_compose2_loop2(): actuation = dpwrap(Mobility(), 'weight', 'actuation_power') battery = dpwrap((BatteryDP(energy_density=100.0)), 'capacity', 'battery_weight') times = dpwrap((Product(R_Time, R_Power, R_Energy)), ['mission_time', 'power'], 'energy') # 'times.power >= actuation.actuation_power', c1 = Connection('actuation', 'actuation_power', 'times', 'power') c2 = Connection('times', 'energy', 'battery', 'capacity') x = dpconnect(dict(actuation=actuation, times=times, battery=battery), [c1,c2]) y = dploop0(x, 'battery_weight', 'weight') print y.desc() assert y.get_fnames() == ['mission_time'], y.get_fnames() assert y.get_rnames() == ['battery_weight'], y.get_rnames() check_ftype(x, 'mission_time', R_Time) # check_ftype(x, 'weight', R_Weight) check_rtype(x, 'battery_weight', R_Weight_g) dp = y.get_dp() funsp = dp.get_fun_space() ressp = dp.get_res_space() print('funsp: %s' % funsp) print('ressp: %s' % ressp) assert funsp == R_Time, funsp assert ressp == R_Weight_g, ressp
def connect_resources_to_outside(name2ndp, connections, ndp_name, rnames): """ For each function in fnames of ndp_name, create a new outside function node and connect it to ndp_name. """ assert ndp_name in name2ndp ndp = name2ndp[ndp_name] if not set(rnames).issubset(ndp.get_rnames()): msg = 'Some of the resources are not present.' raise_desc(ValueError, msg, rnames=rnames, ndp=ndp) for rn in rnames: nn = get_name_for_res_node(rn) R = ndp.get_rtype(rn) name2ndp[nn] = dpwrap(Identity(R), rn, rn) connections.append(Connection(ndp_name, rn, nn, rn))
def connect_resources_to_outside(name2ndp, connections, ndp_name, rnames): """ For each function in fnames of ndp_name, create a new outside function node and connect it to ndp_name. """ assert ndp_name in name2ndp ndp = name2ndp[ndp_name] if not set(rnames).issubset(ndp.get_rnames()): msg = "Some of the resources are not present." raise_desc(ValueError, msg, rnames=rnames, ndp=ndp) for rn in rnames: nn = get_name_for_res_node(rn) R = ndp.get_rtype(rn) name2ndp[nn] = dpwrap(Identity(R), rn, rn) connections.append(Connection(ndp_name, rn, nn, rn))
def connect_functions_to_outside(name2ndp, connections, ndp_name, fnames): """ For each function in fnames of ndp_name, create a new outside function node and connect it to ndp_name. """ assert ndp_name in name2ndp ndp = name2ndp[ndp_name] if not set(fnames).issubset(ndp.get_fnames()): msg = 'Some of the functions are not present.' raise_desc(ValueError, msg, fnames=fnames, ndp=ndp) for fname in fnames: F = ndp.get_ftype(fname) nn = get_name_for_fun_node(fname) name2ndp[nn] = dpwrap(Identity(F), fname, fname) connections.append(Connection(nn, fname, ndp_name, fname))
def connect_functions_to_outside(name2ndp, connections, ndp_name, fnames): """ For each function in fnames of ndp_name, create a new outside function node and connect it to ndp_name. """ assert ndp_name in name2ndp ndp = name2ndp[ndp_name] if not set(fnames).issubset(ndp.get_fnames()): msg = "Some of the functions are not present." raise_desc(ValueError, msg, fnames=fnames, ndp=ndp) for fname in fnames: F = ndp.get_ftype(fname) nn = get_name_for_fun_node(fname) name2ndp[nn] = dpwrap(Identity(F), fname, fname) connections.append(Connection(nn, fname, ndp_name, fname))
def get_approx_dp(S, name, approx_perc, approx_abs, approx_abs_S, max_value, max_value_S): from mcdp_posets.types_universe import express_value_in_isomorphic_space approx_abs_ = express_value_in_isomorphic_space(S1=approx_abs_S, s1=approx_abs, S2=S) max_value_ = express_value_in_isomorphic_space(S1=max_value_S, s1=max_value, S2=S) if approx_perc > 0: raise_desc(DPNotImplementedError, 'Approx_perc not implemented') dps = [] if max_value > 0: dp_max = FuncNotMoreThan(S, max_value_) dps.append(dp_max) if approx_abs_ > 0: dps.append(makeLinearCeilDP(S, approx_abs_)) dp = wrap_series(S, dps) ndp = dpwrap(dp, name, name) return ndp
def compact_context(context): """ If there are two subs with multiple connections, we take the product of their wires. """ from .context_functions import find_nodes_with_multiple_connections from mcdp_dp import Mux from mocdp.comp.wrap import dpwrap from mocdp.comp.connection import connect2 s = find_nodes_with_multiple_connections(context) if not s: return context else: name1, name2, their_connections = s[0] logger.debug('Will compact %s, %s, %s' % s[0]) # establish order their_connections = list(their_connections) s1s = [c.s1 for c in their_connections] s2s = [c.s2 for c in their_connections] # print 'compacting', their_connections ndp1 = context.names[name1] ndp2 = context.names[name2] sname = '_'.join(sorted(s1s)) # space -- [mux] -- R -- [demux] space = ndp1.get_rtypes(s1s) N = len(their_connections) mux = Mux(space, [list(range(N))]) muxndp = dpwrap(mux, s1s, sname) R = mux.get_res_space() coords = [(0, i) for i in range(N)] demux = Mux(R, coords) R2 = demux.get_res_space() assert space == R2, (space, R2) # example: R = PosetProduct((PosetProduct((A, B, C)),)) # demuxndp = dpwrap(demux, sname, s2s) replace1 = connect2(ndp1, muxndp, connections=set([Connection('*', s, '*', s) for s in s1s]), split=[], repeated_ok=False) replace2 = connect2(demuxndp, ndp2, connections=set([Connection('*', s, '*', s) for s in s2s]), split=[], repeated_ok=False) context.names[name1] = replace1 context.names[name2] = replace2 context.connections = [x for x in context.connections if not x in their_connections] c = Connection(name1, sname, name2, sname) context.connections.append(c) return compact_context(context)
def compact_context(context): """ If there are two subs with multiple connections, we take the product of their wires. """ from .context_functions import find_nodes_with_multiple_connections from mcdp_dp import Mux from mocdp.comp.wrap import dpwrap from mocdp.comp.connection import connect2 s = find_nodes_with_multiple_connections(context) if not s: return context else: name1, name2, their_connections = s[0] logger.debug('Will compact %s, %s, %s' % s[0]) # establish order their_connections = list(their_connections) s1s = [c.s1 for c in their_connections] s2s = [c.s2 for c in their_connections] # print 'compacting', their_connections ndp1 = context.names[name1] ndp2 = context.names[name2] sname = '_'.join(sorted(s1s)) # space -- [mux] -- R -- [demux] space = ndp1.get_rtypes(s1s) N = len(their_connections) mux = Mux(space, [list(range(N))]) muxndp = dpwrap(mux, s1s, sname) R = mux.get_res_space() coords = [(0, i) for i in range(N)] demux = Mux(R, coords) R2 = demux.get_res_space() assert space == R2, (space, R2) # example: R = PosetProduct((PosetProduct((A, B, C)),)) # demuxndp = dpwrap(demux, sname, s2s) replace1 = connect2(ndp1, muxndp, connections=set( [Connection('*', s, '*', s) for s in s1s]), split=[], repeated_ok=False) replace2 = connect2(demuxndp, ndp2, connections=set( [Connection('*', s, '*', s) for s in s2s]), split=[], repeated_ok=False) context.names[name1] = replace1 context.names[name2] = replace2 context.connections = [ x for x in context.connections if not x in their_connections ] c = Connection(name1, sname, name2, sname) context.connections.append(c) return compact_context(context)
def cndp_abstract_loop2(ndp): """ Abstracts the dp using the canonical form """ from .composite_makecanonical import get_canonical_elements res = get_canonical_elements(ndp) cycles = res['cycles'] if len(cycles) > 1: msg = ( 'I expected that the cycles were already compacted, while %s remain.' % cycles) raise_desc(NotImplementedError, msg, res=res) inner = res['inner'] inner_dp = inner.get_dp() extraf = res['extraf'] extrar = res['extrar'] # print 'ndp', ndp.get_fnames(), ndp.get_rnames() # print 'inner', inner.get_fnames(), inner.get_rnames() # print 'extra', extraf, extrar # print 'cycles', res['cycles'] assert extraf == ndp.get_fnames(), (extraf, ndp.get_fnames()) assert extrar == ndp.get_rnames(), (extrar, ndp.get_rnames()) # We use the ndp layer to create a dp that has F1 = ndp.get_ftypes(extraf) R1 = ndp.get_rtypes(extrar) # if len(cycles) > 1: # msg = 'Expected there would be at most one cycle, found: %d.' % len(cycles) # raise_desc(Exception, msg, ndp=ndp) if len(cycles) == 0: # raise NotImplementedError() mcdp_dev_warning('this needs much more testing') dp = inner_dp fnames = extraf rnames = extrar if len(fnames) == 1: fnames = fnames[0] if len(rnames) == 1: rnames = rnames[0] from mocdp.comp.wrap import dpwrap return dpwrap(dp, fnames, rnames) F2 = inner.get_rtype(cycles[0]) R2 = F2 dp0F = PosetProduct((F1, F2)) coords1 = [] for inner_fname in inner.get_fnames(): if inner_fname in extraf: coords1.append((0, extraf.index(inner_fname))) else: coords1.append(1) if len(coords1) == 1: coords1 = coords1[0] mux1 = Mux(dp0F, coords1) assert mux1.get_res_space() == inner_dp.get_fun_space() mux0F = inner_dp.get_res_space() coords2extra = [] for rname in extrar: i = inner.get_rnames().index(rname) if len(inner.get_rnames()) == 1: i = () coords2extra.append(i) j = inner.get_rnames().index(cycles[0]) if len(inner.get_rnames()) == 1: j = () coords2 = [coords2extra, j] mux2 = Mux(mux0F, coords2) dp0 = make_series(make_series(mux1, inner_dp), mux2) dp0R_expect = PosetProduct((R1, R2)) assert dp0.get_res_space() == dp0R_expect dp = DPLoop2(dp0) # this is what we want to obtain at the end F = ndp.get_ftypes(ndp.get_fnames()) if len(ndp.get_fnames()) == 1: F = F[0] R = ndp.get_rtypes(ndp.get_rnames()) if len(ndp.get_rnames()) == 1: R = R[0] if len(extraf) == 1: dp = make_series(Mux(F, [()]), dp) if len(extrar) == 1: dp = make_series(dp, Mux(PosetProduct((R, )), 0)) tu = get_types_universe() tu.check_equal(dp.get_fun_space(), F) tu.check_equal(dp.get_res_space(), R) fnames = extraf rnames = extrar if len(fnames) == 1: fnames = fnames[0] if len(rnames) == 1: rnames = rnames[0] # now dp has extra (1) and (1) return SimpleWrap(dp, fnames=fnames, rnames=rnames)
def cndp_abstract_loop2(ndp): """ Abstracts the dp using the canonical form """ from .composite_makecanonical import get_canonical_elements res = get_canonical_elements(ndp) cycles = res['cycles'] if len(cycles) > 1: msg = ('I expected that the cycles were already compacted, while %s remain.' % cycles) raise_desc(NotImplementedError, msg, res=res) inner = res['inner'] inner_dp = inner.get_dp() extraf = res['extraf'] extrar = res['extrar'] # print 'ndp', ndp.get_fnames(), ndp.get_rnames() # print 'inner', inner.get_fnames(), inner.get_rnames() # print 'extra', extraf, extrar # print 'cycles', res['cycles'] assert extraf == ndp.get_fnames(), (extraf, ndp.get_fnames()) assert extrar == ndp.get_rnames(), (extrar, ndp.get_rnames()) # We use the ndp layer to create a dp that has F1 = ndp.get_ftypes(extraf) R1 = ndp.get_rtypes(extrar) # if len(cycles) > 1: # msg = 'Expected there would be at most one cycle, found: %d.' % len(cycles) # raise_desc(Exception, msg, ndp=ndp) if len(cycles) == 0: # raise NotImplementedError() mcdp_dev_warning('this needs much more testing') dp = inner_dp fnames = extraf rnames = extrar if len(fnames) == 1: fnames = fnames[0] if len(rnames) == 1: rnames = rnames[0] from mocdp.comp.wrap import dpwrap return dpwrap(dp, fnames, rnames) F2 = inner.get_rtype(cycles[0]) R2 = F2 dp0F = PosetProduct((F1,F2)) coords1 = [] for inner_fname in inner.get_fnames(): if inner_fname in extraf: coords1.append((0, extraf.index(inner_fname))) else: coords1.append(1) if len(coords1) == 1: coords1 = coords1[0] mux1 = Mux(dp0F, coords1) assert mux1.get_res_space() == inner_dp.get_fun_space() mux0F = inner_dp.get_res_space() coords2extra = [] for rname in extrar: i = inner.get_rnames().index(rname) if len(inner.get_rnames()) == 1: i = () coords2extra.append(i) j = inner.get_rnames().index(cycles[0]) if len(inner.get_rnames()) == 1: j = () coords2 = [coords2extra, j] mux2 = Mux(mux0F, coords2) dp0 = make_series(make_series(mux1, inner_dp), mux2) dp0R_expect = PosetProduct((R1, R2)) assert dp0.get_res_space() == dp0R_expect dp = DPLoop2(dp0) # this is what we want to obtain at the end F = ndp.get_ftypes(ndp.get_fnames()) if len(ndp.get_fnames()) == 1: F = F[0] R = ndp.get_rtypes(ndp.get_rnames()) if len(ndp.get_rnames()) == 1: R = R[0] if len(extraf) == 1: dp = make_series(Mux(F, [()]), dp) if len(extrar) == 1: dp = make_series(dp, Mux(PosetProduct((R,)), 0)) tu = get_types_universe() tu.check_equal(dp.get_fun_space(), F) tu.check_equal(dp.get_res_space(), R) fnames = extraf rnames = extrar if len(fnames) == 1: fnames = fnames[0] if len(rnames) == 1: rnames = rnames[0] # now dp has extra (1) and (1) return SimpleWrap(dp, fnames=fnames, rnames=rnames)