PhD

The LaTeX sources of my Ph.D. thesis
git clone https://esimon.eu/repos/PhD.git
Log | Files | Refs | README | LICENSE

render.lua (7926B)


      1 require("io")
      2 require("math")
      3 
      4 local xmlhandler = require("luaxml-mod-handler")
      5 local xmlparser = require("luaxml-mod-xml")
      6 
      7 local CONFUSION_Y_SPREAD = 0.35
      8 
      9 local render = {}
     10 
     11 local function load_xml(path)
     12 	local treehandler = xmlhandler.simpleTreeHandler()
     13 	local xml = xmlparser.xmlParser(treehandler)
     14 
     15 	local file = io.open(path, "r")
     16 	xml:parse(file:read("*a"))
     17 	file:close()
     18 
     19 	return treehandler.root
     20 end
     21 
     22 local function render_number(x)
     23 	local s = tostring(x)
     24 	local r = ""
     25 	local c = 0
     26 	for i=#s,1,-1 do
     27 		if (#s-i) % 3 == 0 and i~=#s then
     28 			r = "\\," .. r
     29 		end
     30 		r = s:sub(i, i) .. r
     31 	end
     32 	return r
     33 end
     34 
     35 local function embeddings_bounds(list, field)
     36 	local values = {}
     37 	for _, item in ipairs(list) do
     38 		table.insert(values, tonumber(item[field]))
     39 	end
     40 	local low = math.floor(math.min(unpack(values))*2)/2
     41 	local high = math.ceil(math.max(unpack(values))*2)/2
     42 	return low, high
     43 end
     44 
     45 function render.embeddings(path)
     46 	local xml = load_xml(path)
     47 	local xmin, xmax = embeddings_bounds(xml.embeddings.embedding, "x")
     48 	local ymin, ymax = embeddings_bounds(xml.embeddings.embedding, "y")
     49 	tex.print([[\begin{tikzpicture}]])
     50 	tex.print([[\begin{axis}[modern, width=50mm,]])
     51 	tex.print([[	xmin=]] .. tostring(xmin) .. [[,]])
     52 	tex.print([[	xmax=]] .. tostring(xmax) .. [[,]])
     53 	tex.print([[	ymin=]] .. tostring(ymin) .. [[,]])
     54 	tex.print([[	ymax=]] .. tostring(ymax) .. [[,]])
     55 	tex.print([[	xtick={]] .. tostring(xmin) .. [[,]] .. tostring(xmin+0.5) .. [[,...,]] .. tostring(xmax) .. [[},]])
     56 	tex.print([[	ytick={]] .. tostring(ymin) .. [[,]] .. tostring(ymin+0.5) .. [[,...,]] .. tostring(ymax) .. [[},]])
     57 	tex.print([[	x tick label style={rotate=90,anchor=east},]])
     58 	tex.print([[	] ]])
     59 	tex.print([[\addplot+ [black, only marks, mark=*, mark options={fill=black}, nodes near coords, point meta=explicit symbolic,]])
     60 	tex.print([[	coordinate style/.condition={x<0}{anchor=west},]])
     61 	tex.print([[	coordinate style/.condition={x>0}{anchor=east},]])
     62 	tex.print([[	] coordinates {]])
     63 	for _, embedding in ipairs(xml.embeddings.embedding) do
     64 		tex.print(("(%f, %f) [%s]"):format(embedding.x, embedding.y, embedding.label))
     65 	end
     66 	tex.print([[};]])
     67 	tex.print([[\end{axis}]])
     68 	tex.print([[\end{tikzpicture}%]])
     69 	tex.print([[\def\explainedvarx{]] .. ([[%2.1f\%%]]):format(100*xml.embeddings.explained.x) .. [[}%]])
     70 	tex.print([[\def\explainedvary{]] .. ([[%2.1f\%%]]):format(100*xml.embeddings.explained.y) .. [[}%]])
     71 end
     72 
     73 function render_confusion(path, xorig, xdelta, radius, label, id)
     74 	local xml = load_xml(path)
     75 	for j=1,10 do
     76 		local xpos = xorig+j*xdelta
     77 		tex.print([[\node (confusion-column-]]..id..[[-]]..j..[[) at (]]..xpos..[[, 0) {\scriptsize ]]..(j-1)..[[};]])
     78 	end
     79 	for i, gold in ipairs(xml.confusion.gold) do
     80 		local ypos = i*-CONFUSION_Y_SPREAD
     81 		for j, cell in ipairs(gold.clusters.recall) do
     82 			local xpos = xorig+j*xdelta
     83 			local radius = math.sqrt(cell) * radius
     84 			local content = string.format("%.0f", 100*cell)
     85 			tex.print([[\fill (]]..xpos..[[, ]]..ypos..[[) circle (]]..radius..[[);]])
     86 			tex.print([[\coordinate (confusion-cell-]]..id..[[-]]..i..[[-]]..j..[[) at (]]..xpos..[[, ]]..ypos..[[);]])
     87 		end
     88 	end
     89 	local ypos = -CONFUSION_Y_SPREAD*#xml.confusion.gold - 0.2
     90 	local bwest = xorig +  0.5*xdelta
     91 	local beast = xorig + 10.5*xdelta
     92 	tex.print([[\draw[decorate, decoration={brace, amplitude=5}] (]]..beast..[[, ]]..ypos..[[) -- (]]..bwest..[[, ]]..ypos..[[) node[below, midway, yshift=-1mm] (confusion-model-]]..id..[[) {]]..label..[[};]])
     93 end
     94 
     95 function render_confusion_legend(path)
     96 	local xml = load_xml(path)
     97 	for i, gold in ipairs(xml.confusion.gold) do
     98 		local frequency = string.format("%.2f", 100*gold.relation.frequency)
     99 		if tonumber(gold.relation.frequency) < 0.1 then
    100 			frequency = [[\hphantom{0}]] .. frequency
    101 		end
    102 		local label = nil
    103 		if gold.relation.reversed then
    104 			label = [[\(e_2\) ]] .. gold.relation.surfaceform .. [[ \(e_1\)]]
    105 		else
    106 			label = [[\(e_1\) ]] .. gold.relation.surfaceform .. [[ \(e_2\)]]
    107 		end
    108 		local ypos = i*-CONFUSION_Y_SPREAD
    109 		tex.print([[\node[anchor=west] (confusion-row-]]..i..[[) at (0, ]]..ypos..[[) {\makeatletter\scriptsize{}\ifthesis@presentation\else]]..frequency..[[\% \fi]]..label..[[\ifthesis@presentation\else (\wdrel{]]..gold.relation.identifier..[[})\fi\makeatother};]])
    110 	end
    111 end
    112 
    113 function render.confusions(mdelta, cdelta, radius, path1, label1, path2, label2, path3, label3, path4, label4, intikzpicture)
    114 	if not intikzpicture then
    115 		tex.print([[\begin{tikzpicture}]])
    116 	end
    117 	render_confusion(path1, -4*mdelta, cdelta, radius, label1, 1)
    118 	render_confusion(path2, -3*mdelta, cdelta, radius, label2, 2)
    119 	render_confusion(path3, -2*mdelta, cdelta, radius, label3, 3)
    120 	render_confusion(path4, -1*mdelta, cdelta, radius, label4, 4)
    121 	render_confusion_legend(path1)
    122 	if not intikzpicture then
    123 		tex.print([[\end{tikzpicture}]])
    124 	end
    125 end
    126 
    127 local function degrees_table(dict)
    128 	local table = {}
    129 	for _, value in ipairs(dict) do
    130 		table[tonumber(value.degree)] = tonumber(value.frequency)
    131 	end
    132 	return table
    133 end
    134 
    135 local function degrees_bound(degrees, first, second)
    136 	local upper_bound = math.min(#degrees.indegrees.value, #degrees.outdegrees.value)
    137 	local max_frequency = 0
    138 	local min_frequency = 1
    139 	for degree = 1, upper_bound do
    140 		if first[degree] == nil or second[degree] == nil or first[degree]<1e-5 or second[degree]<1e-5 then
    141 			return degree - 1, min_frequency, max_frequency
    142 		end
    143 		max_frequency = math.max(max_frequency, first[degree], second[degree])
    144 		min_frequency = math.min(min_frequency, first[degree], second[degree])
    145 	end
    146 	return upper_bound, min_frequency, max_frequency
    147 end
    148 
    149 local function degrees_true_max(inds, outds)
    150 	local maximum = 0
    151 	local mtype = "in"
    152 	for degree, count in pairs(inds) do
    153 		maximum = math.max(maximum, degree)
    154 	end
    155 	local inter_max = maximum
    156 	for degree, count in pairs(outds) do
    157 		maximum = math.max(maximum, degree)
    158 	end
    159 	if maximum > inter_max then
    160 		mtype= "out"
    161 	end
    162 	return maximum, mtype
    163 end
    164 
    165 function render.degrees(path)
    166 	local xml = load_xml(path)
    167 	local indegrees = degrees_table(xml.degrees.indegrees.value)
    168 	local outdegrees = degrees_table(xml.degrees.outdegrees.value)
    169 	local max_degree, min_frequency, max_frequency = degrees_bound(xml.degrees, indegrees, outdegrees)
    170 	local min_frequency = math.pow(10, math.floor(math.log(min_frequency, 10)))
    171 	local max_frequency = math.pow(10, math.ceil(math.log(max_frequency, 10)))
    172 	local right_degree = math.pow(10, math.ceil(math.log(max_degree, 10)))
    173 
    174 	tex.print([[\begin{tikzpicture}]])
    175 	tex.print([[\begin{loglogaxis}[modern, width=45mm,]])
    176 	tex.print([[	legend entries={in-degree, out-degree},]])
    177 	tex.print([[	legend columns=2,]])
    178 	tex.print([[	legend style={at={(1,1.05)}, anchor=south east, draw=none},]])
    179 	tex.print([[	xlabel={degree},]])
    180 	tex.print([[	ylabel={frequency},]])
    181 	tex.print([[	xmin=1,]])
    182 	tex.print([[	xmax=]] .. tostring(right_degree) .. [[,]])
    183 	tex.print([[	ymin=]] .. tostring(min_frequency) .. [[,]])
    184 	tex.print([[	ymax=]] .. tostring(max_frequency) .. [[,]])
    185 	tex.print([[	ytick={1e-1, 1e-2, 1e-3, 1e-4, 1e-5},]])
    186 	tex.print([[	] ]])
    187 	tex.print([[\addplot+ [Dark2-A,]])
    188 	tex.print([[	mark=none,]])
    189 	tex.print([[	] coordinates {]])
    190 	for degree = 1, max_degree do
    191 		tex.print(("(%f, %f)"):format(degree, indegrees[degree]))
    192 	end
    193 	tex.print([[};]])
    194 	tex.print([[\addplot+ [Dark2-B,]])
    195 	tex.print([[	mark=none,]])
    196 	tex.print([[	] coordinates {]])
    197 	for degree = 1, max_degree do
    198 		tex.print(("(%f, %f)"):format(degree, outdegrees[degree]))
    199 	end
    200 	tex.print([[};]])
    201 	tex.print([[\end{loglogaxis}]])
    202 	tex.print([[\end{tikzpicture}%]])
    203 	tex.print([[\def\numberarcs{]] .. render_number(xml.degrees.m) .. [[}%]])
    204 	tex.print([[\def\maxdisplayeddegree{]] .. render_number(max_degree) .. [[}%]])
    205 
    206 	local true_max_degree, true_max_type = degrees_true_max(indegrees, outdegrees)
    207 	tex.print([[\def\maxdegree{]] .. render_number(true_max_degree) .. [[}%]])
    208 	tex.print([[\def\maxdegreetype{]] .. true_max_type .. [[}%]])
    209 end
    210 
    211 return render