Skip to main content

list manipulation - Compile with array operations


When I use the function f with only the Module


f[a1_, a2_, a3_]:= Module[.....]
f[0,0,1200]


works fine but gives errors when I use compile as follows.


demand[n_, k_, qr_] := Min[k Vf, n capacity - qr]; supply[n_, k_, qr_] := Min[(n Kj - k) w, n capacity - qr];
Nsupply[n_, k_, qsum_, qrsum_, qr_] := Min[(n Kj - k) Vf - qsum - qrsum, n capacity - qr];
floExct[n_, ku_, k_, qsum_, qrsum_, qr_] := Min[demand[n, ku, qr], Nsupply[n, k, qsum, qrsum, qr]];
gamma[ku_, kd_] := Min[1, supply[L, kd, 0]/(demand[L, ku, 0] + 0.001)];
inflow[phi_, qin_] := (phi - \[Beta] qin) dx;
density[qin_, qout_, qr_] := (qin - qout + qr)/Vf;

Kj = 150.; w = 20.; Vf = 100.; capacity = 2500.; n = 30; m = 100; p = 27; RML = 18; \[Alpha] = 1200.; \[Beta] = 0.; L = 1.; delta = 1.; dt =4./3600.; dx = 1./9.;


f= Compile[{{a1, _Integer}, {a2, _Integer}, {a3, _Integer}},
Module[{k0 = ConstantArray[0., n],
Fk = Table[Table[0., {i1, n}], {j, 5}],
kr = Table[Table[0., {i1, 1, p}], {i2, 1, n - 2}],
Rk = Table[Table[Table[0., {i1, 1, p}], {i2, 1, n - 2}], {i3, 4}], \[Phi],
Fq = Table[Table[0., {i1, n - 1}], {j, 4}], qin,
Fin = Table[Table[0., {i1, n - 2}], {j, 4}], qr,
Rq = Table[Table[Table[0., {i1, 1, p}], {i2, 1, n - 2}], {i3, 4}],
RMori = Table[0., {i1, 1, n - 2}],
RM = Table[0., {i1, 1, n - 2}], TT = 0., qf, qsum, qrsum},

RMori = Table[a1 (i1 dx)^2 + a2 (i1 dx) + a3, {i1, 1, n - 2}];
kr = ReplacePart[#, (\[Alpha] delta/Vf), 1] & /@ kr; AppendTo[Rk, kr];

\[Phi] = demand[1, #[[-1]], 0] & /@ kr MapThread[gamma, {Take[k0, {2, -2}], Take[k0, {3, -1}]}];
qsum = N[Plus @@ Fq];
qrsum = Join[Plus @@ Fin, {0}];
qf = MapThread[floExct[L, #1, #2, #3, #4, #5] &, {Most@k0, Rest@(Fk[[1]]), qsum,qrsum, Join[\[Phi], {0}]}];
qin = inflow[\[Phi], Most@qf];
k0 += Join[{0}, density[Most@qf, Rest@qf, qin], {0}];
qsum = MapThread[Plus, #] &@Rq;

RM = MapThread[Min[#1, #2] &, {RMori, MapThread[floExct[1, #1[[RML]], #2[[RML + 1]], #3[[RML]], 0, 0] &, {kr,Rk[[1]], qsum}]}];
qr = MapThread[Join[#1, {#2}, #3, {#4}] &, {
MapThread[floExct[1, #1, #2, #3, 0, 0] &, {Take[#, {1, RML - 1}] & /@ kr,Take[#, {2, RML}] & /@ Rk[[1]], Take[#, {1, RML - 1}] & /@ qsum}, 2], RM,
MapThread[floExct[1, #1, #2, #3, 0, 0] &, {Take[#, {RML + 1, -2}] & /@ kr, Take[#, {RML + 2, -1}] & /@ Rk[[1]], Take[#, {RML + 1, -2}] & /@ qsum}, 2], \[Phi]}];
kr = MapThread[(#1 + #2) &, {Join[{0}, #] & /@ (density[Most@#, Rest@#, 0] & /@ qr), kr}];

TT += Plus @@ k0; TT += Plus @@ (Plus @@ kr);

Fq = Delete[Fq, {1}]; Fin = Delete[Fin, {1}]; Rq = Delete[Rq, {1}]; Fk = Delete[Fk, {1}]; Rk = Delete[Rk, {1}];
AppendTo[Fq, qf]; AppendTo[Fin, qin]; AppendTo[Rq, qr]; AppendTo[Fk, k0]; AppendTo[Rk, kr];

TT]]

all the errors (as shown below) are related to the qsum variable. Can someone tell me what I am missing?


MapThread::list: List expected at position 2 in MapThread[Plus,Compile`FunctionVariable$266]. >>

Compile::cprank: Compile cannot determine the rank of the result tensor. The length of CompileFunctionVariable$266 in MapThread[Plus,Compile`FunctionVariable$266] is unknown at compiling time; evaluation will use the uncompiled function. >> Compile::cset: Variable qsum of type {_Real,1} encountered in assignment of type {_Integer,0}. >> Compile::part: Part specification CompileGetElement[qsum,CompileVariable$912][[18]] cannot be compiled since the argument is not a tensor of sufficient rank. Evaluation will use the uncompiled function. >> Compile::part: Part specification Compile`GetElement[qsum,Compile`Variable$912][[18]] cannot be compiled since the argument is not a tensor of sufficient rank. Evaluation will use the uncompiled function. >> Compile::part: Part specification CompileGetElement[qsum,CompileVariable$1852][[18]] cannot be compiled since the argument is not a tensor of sufficient rank. Evaluation will use the uncompiled function. >> General::stop: Further output of Compile::part will be suppressed during this calculation. >> Compile::cplist: Compile`GetElement[qsum,System`Private`CompileSymbol[0]] should be a tensor of type Integer, Real, or Complex; evaluation will use the uncompiled function. >> Compile::cplist: Compile`GetElement[qsum,System`Private`CompileSymbol[0]] should be a tensor of type Integer, Real, or Complex; evaluation will use the uncompiled function. >> Compile::cplist: Compile`GetElement[qsum,System`Private`CompileSymbol[0]] should be a tensor of type Integer, Real, or Complex; evaluation will use the uncompiled function. >> General::stop: Further output of Compile::cplist will be suppressed during this calculation. >> MapThread::list: List expected at position 2 in MapThread[Plus,Compile`FunctionVariable$3193]. >> Compile::cprank: Compile cannot determine the rank of the result tensor. The length of CompileFunctionVariable$3193 in MapThread[Plus,Compile`FunctionVariable$3193] is unknown at compiling time; evaluation will use the uncompiled function. >> Compile::cset: Variable qsum of type {_Real,1} encountered in assignment of type {_Integer,0}. >>


Edit:


Please note that the above code is a simplified/partial piece of a larger code. I presented this to show the problem area. The original code has several hundred iterations (till a condition is met) of the above critical steps and the compiled function itself will be called several thousands of times (for direct search-based optimization).


Edit 2:


Thanks to xzczd, the following code compiles without any errors. However, it gives error/warnings during run-time. The code may look cumbersome, but the problem area is very specific.



f = With[{Kj = 150., w = 20., Vf = 100., capacity = 2500., n = 15, m = 30, p = 10, RML = 5, \[Alpha] = 1200., \[Beta] = 0., L = 1., delta = 1., dt = 4./3600., dx = 1./9.},
Module[{demand, supply, Nsupply, floExct, gamma, inflow, density}, demand = Min[#2 Vf, # capacity - #3] &; supply = Min[(# Kj - #2) w, # capacity - #3] &; Nsupply = Min[(# Kj - #2) Vf - #3 - #4, # capacity - #5] &;
floExct = Evaluate@Min[demand[#, #2, #6], Nsupply[#, #3, #4, #5, #6]] &; gamma = Evaluate@Min[1, supply[L, #2, 0]/(demand[L, #, 0] + 0.001)] &; inflow = (# - \[Beta] #2) dx &; density = (# - #2 + #3)/Vf &;
Compile[{{a1, _Integer}, {a2, _Integer}, {a3, _Integer}},
Module[{k0 = Table[0., {n}], Fk = Table[0., {5}, {n}], kr = Table[0., {n - 2}, {p}], Rk = Table[0., {4}, {n - 2}, {p}], Fq = Table[0., {4}, {n - 1}], Fin = Table[0., {4}, {n - 2}], Rq = Table[0., {4}, {n - 2}, {p}],
RMori = Table[a1 (i1 dx)^2 + a2 (i1 dx) + a3, {i1, 1, n - 2}], Shutoff = False, j = 0, \[Phi], qin, qr, qf, qsum, qrsum, RM, TT, NtwrkTT},
If[300 > Min[RMori] && Max[RMori] > 1200, Print["MR not within bounds"]; NtwrkTT = 10^20,
kr = ReplacePart[#, (\[Alpha] delta/Vf), 1] & /@ kr;
NtwrkTT = TT = Total@(Total@kr); AppendTo[Rk, kr];
While[TT > 0, TT = 0;

\[Phi] = demand[1, #[[-1]], 0] & /@ kr MapThread[gamma, {Take[k0, {2, -2}], Take[k0, {3, -1}]}];
qrsum = Join[Total@Fin, {0}];
qf = MapThread[floExct[L, #1, #2, #3, #4, #5] &, {Most@k0, Rest@(Fk[[1]]), Total@Fq, qrsum, Join[\[Phi], {0}]}];
qin = inflow[\[Phi], Most@qf];
k0 += Join[{0}, density[Most@qf, Rest@qf, qin], {0}];
qsum = Total@Rq;
RM = MapThread[Min[#1, #2] &, {RMori, MapThread[floExct[1, #1[[RML]], #2[[RML + 1]], #3[[RML]], 0, 0] &, {kr, Rk[[1]], qsum}]}];
qr = MapThread[Join[#1, {#2}, #3, {#4}] &, {MapThread[floExct[1, #1, #2, #3, 0, 0] &, {Take[#, {1, RML - 1}] & /@ kr, Take[#, {2, RML}] & /@ Rk[[1]], Take[#, {1, RML - 1}] & /@ qsum}, 2], RM,
MapThread[floExct[1, #1, #2, #3, 0, 0] &, {Take[#, {RML + 1, -2}] & /@ kr, Take[#, {RML + 2, -1}] & /@ Rk[[1]], Take[#, {RML + 1, -2}] & /@ qsum}, 2], \[Phi]}];
kr = MapThread[(#1 + #2) &, {Join[{0}, #] & /@ (density[Most@#, Rest@#, 0] & /@ qr), kr}];

TT += Total@k0; TT += Total@(Total@kr); NtwrkTT += TT; j++;
Fq = Delete[Fq, {1}]; Fin = Delete[Fin, {1}]; Rq = Delete[Rq, {1}]; Fk = Delete[Fk, {1}]; Rk = Delete[Rk, {1}];
AppendTo[Fq, qf]; AppendTo[Fin, qin]; AppendTo[Rq, qr]; AppendTo[Fk, k0]; AppendTo[Rk, kr];
Print["j: ", j];
If[#[[2]] > Kj - \[Alpha]/w, Print["Ramp Spillback"]; NtwrkTT = 10^20; Break[]] & /@ kr;
If[(Shutoff == False) && j > m, Shutoff = True; kr = ReplacePart[#, 0, 1] & /@ kr];
];
]; NtwrkTT dt], CompilationOptions -> {"InlineExternalDefinitions" -> True}]]];

when I run the compiled function with f[0,0,1100], it runs the While loop only once, gives the following error:



CompiledFunction::cfse: Compiled expression {Null,Null,Null,Null,Null,Null,Null,Null,Null,Null,Null,Null,Null} should be a machine-size real number. >>
CompiledFunction::cfex: Could not complete external evaluation at instruction 491; proceeding with uncompiled evaluation. >>

Then, it proceeds to run the program, I guess in an uncompiled fashion.


One possible variable that refers to {Null,Null,Null,Null,Null,Null,Null,Null,Null,Null,Null,Null,Null} may be RM and I was wondering, if it is the source of problem.



Answer



OK, as an after-meal exercise, I fixed your code. The main modifications I've done are:


1. Based on trick mentioned in this post, change your pattern-matching function into pure function.


2. Change qsum = MapThread[Plus, #] &@Rq(for some unclear reason it can't be compiled, maybe it's because Rq isn't a "explicit" list? ) into qsum=Total@Rq.


3. Introduce constants with With to avoid external calls in Compile caused by unlocalized variables. (Explanation for this involves explanation for how With works, which I'd like to omit here. There already exists some good posts for this issue in this site, for example this. )



4. Embed qsum = N[Plus @@ Fq] into the definition of qf to avoid error (This seems to be because the dimension of qsum will be changed in latter code if we don't eliminate it here ).


Note: I've discard my suggestion in the comment above of using kr[[All, 1]] = α delta/Vf instead of kr = ReplacePart[#, (α delta/Vf), 1] & /@ kr because I found the latter can be compiled with the modifications mentioned above, what's more, kr[[All, 1]] = α delta/Vf can't be compiled, kr[[All, 1]] = Table[α delta/Vf, {n - 2}] can, though.


The following will generate no error and be about 20 times faster than the corresponding uncompiled version:


f = With[{Kj = 150., w = 20., Vf = 100., capacity = 2500., n = 30, 
m = 100, p = 27, RML = 18, α = 1200., β = 0., L = 1.,
delta = 1., dt = 4./3600., dx = 1./9.},
Module[{demand, supply, Nsupply, floExct, gamma, inflow, density},
demand = Min[#2 Vf, # capacity - #3] &;
supply = Min[(# Kj - #2) w, # capacity - #3] &;
Nsupply = Min[(# Kj - #2) Vf - #3 - #4, # capacity - #5] &;

floExct = Evaluate@Min[demand[#, #2, #5], Nsupply[#, #3, #4, #5, #6]] &;
gamma = Evaluate@Min[1, supply[L, #2, 0]/(demand[L, #, 0] + 0.001)] &;
inflow = (# - β #2) dx &;
density = (# - #2 + #3)/Vf &;
Compile[{{a1, _Integer}, {a2, _Integer}, {a3, _Integer}},
Module[{k0 = Table[0., {n}],
Fk = Table[0., {5}, {n}],
kr = Table[0., {n - 2}, {p}],
Rk = Table[0., {4}, {n - 2}, {p}], ϕ,
Fq = Table[0., {4}, {n - 1}], qin,

Fin = Table[0., {4}, {n - 2}], qr,
Rq = Table[0., {4}, {n - 2}, {p}],
RMori = Table[a1 (i1 dx)^2 + a2 (i1 dx) + a3, {i1, 1, n - 2}],
RM = Table[0., {n - 2}], TT = 0., qf, qsum, qrsum},

kr = ReplacePart[#, (α delta/Vf), 1] & /@ kr;
AppendTo[Rk, kr];
ϕ =
demand[1, #[[-1]], 0] & /@ kr MapThread[
gamma, {Take[k0, {2, -2}], Take[k0, {3, -1}]}];

qrsum = Join[Plus @@ Fin, {0}];
qf =
MapThread[
floExct[L, #1, #2, #3, #4, #5] &, {Most@k0, Rest@(Fk[[1]]),
Plus @@ Fq, qrsum, Join[ϕ, {0}]}];
qin = inflow[ϕ, Most@qf];
k0 += Join[{0}, density[Most@qf, Rest@qf, qin], {0}];
qsum = Total@Rq;
RM =
MapThread[

Min[#1, #2] &, {RMori,
MapThread[
floExct[1, #1[[RML]], #2[[RML + 1]], #3[[RML]], 0,
0] &, {kr, Rk[[1]], qsum}]}];
qr =
MapThread[
Join[#1, {#2}, #3, {#4}] &, {MapThread[
floExct[1, #1, #2, #3, 0, 0] &, {Take[#, {1, RML - 1}] & /@
kr, Take[#, {2, RML}] & /@ Rk[[1]],
Take[#, {1, RML - 1}] & /@ qsum}, 2], RM,

MapThread[
floExct[1, #1, #2, #3, 0, 0] &, {Take[#, {RML + 1, -2}] & /@
kr, Take[#, {RML + 2, -1}] & /@ Rk[[1]],
Take[#, {RML + 1, -2}] & /@ qsum}, 2], ϕ}];
kr =
MapThread[(#1 + #2) &, {Join[{0}, #] & /@ (density[Most@#,
Rest@#, 0] & /@ qr), kr}];
TT += Plus @@ k0; TT += Plus @@ (Plus @@ kr);
Fq = Delete[Fq, {1}]; Fin = Delete[Fin, {1}];
Rq = Delete[Rq, {1}]; Fk = Delete[Fk, {1}]; Rk = Delete[Rk, {1}];

AppendTo[Fq, qf]; AppendTo[Fin, qin]; AppendTo[Rq, qr];
AppendTo[Fk, k0]; AppendTo[Rk, kr];
TT],
CompilationOptions -> {"InlineExternalDefinitions" -> True}]]];

I believe this function can still be optimized because many CopyTensor, which is said to severely influence the performance of compiled function, exists in the output of CompilePrint, but this time I'll really stop and leave it to you. As for the resource of this issue, I think you've already found this post, there exist discussion for CopyTensor, just search CopyTensor inside the page, there exist other posts about the issue in this site too, for example this, you can have a search.




Update:


To fix your final code, I made the following modifications:


1. Change all the NtwrkTT = 10^20 into NtwrkTT = 10^20.. (Though you mentioned the code is compiled without error in your computer, it fails without this modification in my v8.0.4, Windows Vista Home Basic 32bit. )



2. Change


If[#[[2]] > Kj - α/w, Print["Ramp Spillback"]; NtwrkTT = 10^20; Break[]] & /@ kr;

into


Do[If[i[[2]] > Kj - α/w, Print["Ramp Spillback"]; NtwrkTT = 10^20; Break[]], {i, kr}];

Using Map and Apply inside Compile needs corresponding mental preparation: It can easily cause failure. (At one time I even thought that "The only function arguments supported are Times, Plus, or List", but your code has already shown it's not correct.)


The fixed code is as following:


f = With[{Kj = 150., w = 20., Vf = 100., capacity = 2500., n = 15, 
m = 30, p = 10, RML = 5, α = 1200., β = 0., L = 1.,

delta = 1., dt = 4./3600., dx = 1./9.},
Module[{demand, supply, Nsupply, floExct, gamma, inflow, density},
demand = Min[#2 Vf, # capacity - #3] &;
supply = Min[(# Kj - #2) w, # capacity - #3] &;
Nsupply = Min[(# Kj - #2) Vf - #3 - #4, # capacity - #5] &;
floExct = Evaluate@Min[demand[#, #2, #6], Nsupply[#, #3, #4, #5, #6]] &;
gamma = Evaluate@Min[1, supply[L, #2, 0]/(demand[L, #, 0] + 0.001)] &;
inflow = (# - β #2) dx &;
density = (# - #2 + #3)/Vf &;
Compile[{{a1, _Integer}, {a2, _Integer}, {a3, _Integer}},

Module[{
k0 = Table[0., {n}],
Fk = Table[0., {5}, {n}],
kr = Table[0., {n - 2}, {p}],
Rk = Table[0., {4}, {n - 2}, {p}],
Fq = Table[0., {4}, {n - 1}],
Fin = Table[0., {4}, {n - 2}],
Rq = Table[0., {4}, {n - 2}, {p}],
RMori = Table[a1 (i1 dx)^2 + a2 (i1 dx) + a3, {i1, 1, n - 2}],
Shutoff = False, j = 0, ϕ, qin, qr, qf, qsum, qrsum, RM, TT, NtwrkTT},

If[300 > Min[RMori] && Max[RMori] > 1200,
Print["MR not within bounds"]; NtwrkTT = 10^20.,
kr = ReplacePart[#, (α delta/Vf), 1] & /@ kr;
NtwrkTT = TT = Total@(Total@kr); AppendTo[Rk, kr];
While[TT > 0, TT = 0;
ϕ =
demand[1, #[[-1]], 0] & /@ kr MapThread[
gamma, {Take[k0, {2, -2}], Take[k0, {3, -1}]}];
qrsum = Join[Total@Fin, {0}];
qf =

MapThread[
floExct[L, #1, #2, #3, #4, #5] &, {Most@k0, Rest@(Fk[[1]]),
Total@Fq, qrsum, Join[ϕ, {0}]}];
qin = inflow[ϕ, Most@qf];
k0 += Join[{0}, density[Most@qf, Rest@qf, qin], {0}];
qsum = Total@Rq;
RM =
MapThread[
Min[#1, #2] &, {RMori,
MapThread[

floExct[1, #1[[RML]], #2[[RML + 1]], #3[[RML]], 0,
0] &, {kr, Rk[[1]], qsum}]}];
qr =
MapThread[
Join[#1, {#2}, #3, {#4}] &, {MapThread[

floExct[1, #1, #2, #3, 0, 0] &, {Take[#, {1, RML - 1}] & /@
kr, Take[#, {2, RML}] & /@ Rk[[1]],
Take[#, {1, RML - 1}] & /@ qsum}, 2], RM,
MapThread[

floExct[1, #1, #2, #3, 0,
0] &, {Take[#, {RML + 1, -2}] & /@ kr,
Take[#, {RML + 2, -1}] & /@ Rk[[1]],
Take[#, {RML + 1, -2}] & /@ qsum}, 2], ϕ}];
kr =
MapThread[(#1 + #2) &, {Join[{0}, #] & /@ (density[Most@#,
Rest@#, 0] & /@ qr), kr}];
TT += Total@k0; TT += Total@(Total@kr); NtwrkTT += TT; j++;
Fq = Delete[Fq, {1}]; Fin = Delete[Fin, {1}];
Rq = Delete[Rq, {1}]; Fk = Delete[Fk, {1}];

Rk = Delete[Rk, {1}];
AppendTo[Fq, qf]; AppendTo[Fin, qin]; AppendTo[Rq, qr];
AppendTo[Fk, k0]; AppendTo[Rk, kr];
Print["j: ", j];
Do[If[i[[2]] > Kj - α/w, Print["Ramp Spillback"];
NtwrkTT = 10^20; Break[]], {i, kr}];
If[(Shutoff == False) && j > m, Shutoff = True;
kr = ReplacePart[#, 0, 1] & /@ kr];];]; NtwrkTT dt],
CompilationOptions -> {"InlineExternalDefinitions" -> True}]]];


At the end of this post, I'd like to say something about Compile. Generally speaking, though I inevitably use Compile in my task, I think it's really a black sheep inside Mathematica, not because its limitation for one's programming ideas, but its elusiveness. Your code is finally speed up by Compile, but I believe a better design can make it faster and more elegant and keep it away from the endless grief looking for the part that isn't compiled. Since your original code has become quite large, this suggestion might be a little late, but I really recommend writing your program in a more… mathematica-like way in your next task.


Comments

Popular posts from this blog

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Mathematica: 3D plot based on combined 2D graphs

I have several sigmoidal fits to 3 different datasets, with mean fit predictions plus the 95% confidence limits (not symmetrical around the mean) and the actual data. I would now like to show these different 2D plots projected in 3D as in but then using proper perspective. In the link here they give some solutions to combine the plots using isometric perspective, but I would like to use proper 3 point perspective. Any thoughts? Also any way to show the mean points per time point for each series plus or minus the standard error on the mean would be cool too, either using points+vertical bars, or using spheres plus tubes. Below are some test data and the fit function I am using. Note that I am working on a logit(proportion) scale and that the final vertical scale is Log10(percentage). (* some test data *) data = Table[Null, {i, 4}]; data[[1]] = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 1.0}, {1, -6.8}, {2, -4.7}, {3, -1....