From cff9149ece0b5c8501e5e584019b27a379955228 Mon Sep 17 00:00:00 2001
From: Fabian Gruber <fabian.gruber@inria.fr>
Date: Mon, 16 Apr 2018 14:41:24 -0600
Subject: [PATCH] added prose description of the ILP model implemented in
 Gurobi.

---
 src/pipedream/ilp/the-ilp-model.txt | 164 ++++++++++++++++++++++++++++
 1 file changed, 164 insertions(+)
 create mode 100644 src/pipedream/ilp/the-ilp-model.txt

diff --git a/src/pipedream/ilp/the-ilp-model.txt b/src/pipedream/ilp/the-ilp-model.txt
new file mode 100644
index 0000000..18aaaf3
--- /dev/null
+++ b/src/pipedream/ilp/the-ilp-model.txt
@@ -0,0 +1,164 @@
+///#############################################################################
+///##### INPUT
+
+///### CPU LAYOUT
+
+/// number of instructions (each is identified by an opaque int)
+int num_insts     = ...;
+
+/// number of muops (each is identified by an opaque int)
+int num_muops     = ...;
+
+/// number of ports (each is identified by an opaque int)
+int num_ports     = ...;
+
+
+///### KERNELS
+
+/// number of kernels that where run
+int num_kernels   = ...;
+
+/// Instruction per Cycle (IPC), i.e. throughput, measured for kernel *k*
+float IPC[k in num_kernels] = ...;
+
+/// how many times instruction *i* appears in kernel *k*
+int occurences[k in num_kernels][i in num_insts] = ...;
+
+
+///#############################################################################
+///##### OUTPUT
+
+/// bipartite graph between instructions and muops
+dvar int graph_im[num_muops][num_ports] in 0..1;
+
+/// bipartite graph between muops and ports
+dvar int graph_mp[num_muops][num_ports] in 0..1;
+
+
+///#############################################################################
+///##### HELPERS
+
+/// throughput per edge (inst, muop) in kernel k
+dvar float throughput_im[num_kernels][num_insts][num_muops];
+
+/// throughput per edge (muop, port) in kernel k
+dvar float throughput_mp[num_kernels][num_insts][num_muops];
+
+/// throughput for muop type i in kernel s
+dexpr float muop_throughput[k in num_kernels][m in num_muops] = sum(p in num_ports) throughput[k][m][p];
+
+/// mean throughput for a given muop type in a kernel
+dexpr float mean[k in num_kernels][m in num_muops] = (1 / num_ports) * sum(p in num_ports) throughput[k][m][p];
+
+/// variance of throughput for a given muop type in a kernel
+dexpr float variance[k in num_kernels][m in num_muops] = (1 / num_ports) *
+  sum(p in num_ports) pow(throughput[k][m][p], 2) - pow(mean[k][m], 2);
+
+
+dexpr int num_im_edges = sum(k in num_kernels) sum(i in num_insts) sum(m in num_muops) delta_mp[k][i][m]
+
+dexpr int num_mp_edges = sum(k in num_kernels) sum(m in num_muops) sum(p in num_ports) delta_mp[k][m][p]
+
+
+//##############################################################################
+//##### OBJECTIVE FUNCTION
+
+dexpr float all_throughputs = sum(k in num_kernels) total_throughput[k];
+
+/// number of edges present in the machine
+dexpr float num_edges = sum(i in Src, p in num_ports) delta_mp[m][p];
+
+dexpr float all_variances = sum(k in num_kernels, i in Src) variance[k][m];
+
+maximize(num_mp_edges - num_im_edges);
+
+
+//##############################################################################
+//##### CONSTRAINTS
+
+subject to {
+  forall(k in num_kernels, i in Src, p in num_ports) {
+    throughput_bounds:
+      0 <= throughput[k][m][p];
+  }
+  forall(k in num_kernels, i in Src, p in num_ports) {
+    throughputs_limited_by_delta_mp:
+      throughput[k][m][p] <= delta_mp[m][p];
+  }
+
+  forall(k in num_kernels, p in num_ports) {
+    port_bandwidth:
+      sum(i in Src) throughput[k][m][p] <= 1;
+  }
+
+  forall(k in num_kernels) {
+    total_throughput_limited_by_ports:
+      total_throughput[k] <= num_ports;
+  }
+
+  forall(k in num_kernels) {
+    total_throughput_is_sum_of_throughputss:
+      total_throughput[k] == sum(i in Src) muop_throughput[k][m];
+  }
+
+#// prints constraints set with OPL label iff constraint text is not empty
+#% macro constraint_set(name)
+#%   set X = caller().strip()
+#%   if X and not X.isspace()
+  {{ name }}:
+    {{ X }}
+#%   endif
+#% endmacro
+
+#// print delta_mp
+#% call constraint_set('port_layout')
+#%   for i, j in model.delta_mp | sort_by_index
+#%     set edge = model.delta_mp[i, j]
+#%     if edge.is_specified
+    delta_mp[{{ i | tuple }}][{{ j | tuple }}] == {{ edge }};
+#%     endif
+#%   endfor
+#% endcall
+
+#// print total throughput per kernel
+#% call constraint_set('observed_total_throughput')
+#%   for seq in model.total_throughput | sort_by_index
+#%     set total_throughput = model.total_throughput[seq]
+#%     if total_throughput.is_specified
+    total_throughput[{{ seq[0] | tuple }}] == {{ total_throughput }};
+#%     endif
+#%   endfor
+#% endcall
+
+#// print throughput per muop/port pair
+#% call constraint_set('observed_throughputs')
+#%   for s, i, j in model.throughput | sort_by_index
+#%     set throughput = model.throughput[s, i, j]
+#%     if throughput.is_specified
+    throughput[{{ s | tuple }}][{{ i | tuple }}][{{ j | tuple }}] == {{ throughput }};
+#%     endif
+#%   endfor
+#% endcall
+
+#// print instruction ratios in kernels
+#% call constraint_set('inst_mix')
+#%   set mix = model.instruction_mix
+#%   for s, i in mix
+#%     set ratio = mix[s, i]
+#%     if ratio == 0
+         #// can't use muop_throughput here because that would use tuple patterns
+         #// and we already use non-pattern constraints elsewhere
+    sum(p in num_ports) throughput[{{ s | tuple }}][{{ i | tuple }}][p] == 0;
+#%     endif
+#%   endfor
+#%   for s in model.kernels
+#%     for i1, i2 in model.muops | combinations(2)
+#%       set r1 = mix[s, i1]
+#%       set r2 = mix[s, i2]
+#%       if r1 and r2
+    {{ r1 }} * sum(p in num_ports) throughput[{{ s | tuple }}][{{ i1 | tuple }}][p] == {{ r2 }} * sum(p in num_ports) throughput[{{ s | tuple }}][{{ i2 | tuple }}][p];
+#%       endif
+#%     endfor
+#%   endfor
+#% endcall
+}
-- 
GitLab