ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
DotCircuit.cpp
Go to the documentation of this file.
1/**
2 * @file DotCircuit.cpp
3 * @brief DotCircuit method implementations and GraphViz rendering.
4 *
5 * Implements:
6 * - @c DotCircuit::addGate(): allocate a gate and extend the @c desc vector.
7 * - @c DotCircuit::setGate(const uuid&, DotGate): create a gate by UUID.
8 * - @c DotCircuit::setGate(const uuid&, DotGate, std::string): create a gate
9 * with a label string stored in @c desc.
10 * - @c DotCircuit::toString(): return the description string for a gate.
11 * - @c DotCircuit::render(): produce a complete GraphViz @c digraph string
12 * with semiring symbols (⊗, ⊕, ⊖, δ) as node labels and the
13 * provenance token UUIDs as edge labels for input gates.
14 */
15#include "DotCircuit.h"
16#include "external_tool.h"
17#include <type_traits>
18
19extern "C"
20{
21#include "provsql_utils.h"
22#include <unistd.h>
23}
24
25#include <cassert>
26#include <string>
27#include <fstream>
28#include <sstream>
29#include <cstdlib>
30
31// Has to be redefined because of name hiding
33{
34 auto id = Circuit::setGate(u, type);
35 if(type == DotGate::IN)
36 inputs.insert(id);
37 return id;
38}
39
40gate_t DotCircuit::setGate(const uuid &u, DotGate type, std::string d)
41{
42 auto id = setGate(u, type);
43 desc[static_cast<std::underlying_type<gate_t>::type>(id)] = d;
44 return id;
45}
46
48{
49 auto id=Circuit::addGate();
50 desc.push_back("");
51 return id;
52}
53
54//Outputs the gate in Graphviz dot format
55std::string DotCircuit::toString(gate_t) const
56{
57 std::string op;
58 std::string result="digraph circuit{\n graph [rankdir=UD] ;\n";
59
60 //looping through the gates
61 //eliminating the minusr and minusl gates
62 unsigned i = 0;
63 for (auto g : gates)
64 {
65 if (g != DotGate::OMINUSR && g != DotGate::OMINUSL)
66 {
67 result += std::to_string(i) + " [label=";
68 switch (g)
69 {
70 case DotGate::IN:
71 result += "\"" + desc[i] + "\"";
72 break;
73 case DotGate::OMINUS:
74 result += "\"⊖\"";
75 break;
77 result += "\"?\"";
78 break;
79 case DotGate::OTIMES:
80 result += "\"⊗\"";
81 break;
82 case DotGate::OPLUS:
83 result += "\"⊕\"";
84 break;
85 case DotGate::DELTA:
86 result += "\"δ\"";
87 break;
88 case DotGate::EQ:
89 result += "\"" + desc[i] + "\"";
90 break;
92 result += "\"Π" + desc[i] + "\"";
93 break;
96 break;
97 }
98 if(i==0)
99 result+=",shape=\"double\"";
100 result+="];\n";
101 }
102 i++;
103 }
104
105 //looping through the gates and their wires
106 for(i=0; i<wires.size(); ++i) {
107 if(gates[i] != DotGate::OMINUSR && gates[i] != DotGate::OMINUSL) {
108 std::unordered_map<gate_t, unsigned> number_gates;
109 for(auto s: wires[i]) {
110 if(number_gates.find(s)!=number_gates.end()) {
111 number_gates[s] = number_gates[s]+1;
112 }
113 else
114 {
115 number_gates[s] = 1;
116 }
117 }
118 for(auto [s,n]: number_gates)
119 {
121 for(auto t: getWires(s)) {
122 result += std::to_string(i)+" -> "+to_string(t);
124 result += " [label=\"R\"];\n";
125 else
126 result += " [label=\"L\"];\n";
127 }
128 }
129 else {
130 result += std::to_string(i)+" -> "+to_string(s);
131 if(n==1) {
132 result += ";\n";
133 }
134 else
135 {
136 result += " [label=\"" + std::to_string(n) + "\"];\n";
137 }
138 }
139 }
140 }
141 }
142 return result + "}";
143}
144
145std::string DotCircuit::render() const {
146 // Use a private 0700 directory rather than a bare mkstemp file so
147 // the deterministically-derived sibling output path (.out) cannot
148 // be raced by a local user pre-creating a symlink before
149 // graph-easy opens it.
150 char cdir[] = "/tmp/provsqlXXXXXX";
151 if(mkdtemp(cdir) == NULL) {
152 throw CircuitException("Cannot create temporary directory");
153 }
154 std::string filename=std::string(cdir)+"/dot", outfilename=filename+".out";
155
156 std::ofstream ofs(filename.c_str());
157 ofs << toString(gate_t{0});
158 ofs.close();
159
160 //Executing the Graphviz dot renderer through graph-easy for ASCII
161 //output
162 if(find_external_tool("graph-easy").empty()) {
163 // best-effort cleanup; ignore failures
164 unlink(filename.c_str());
165 std::string dirname=filename.substr(0, filename.rfind('/'));
166 rmdir(dirname.c_str());
167 throw CircuitException(
168 "graph-easy not found on PATH; install it or add its "
169 "directory to provsql.tool_search_path");
170 }
171
172 std::string cmdline="graph-easy --as=boxart --output="+outfilename+" "+filename;
173
174 int retvalue = run_external_tool(cmdline);
175
176 if(provsql_verbose<20) {
177 if(unlink(filename.c_str())) {
178 throw CircuitException("Error removing "+filename);
179 }
180 }
181
182 if(retvalue)
183 throw CircuitException(format_external_tool_status(retvalue, "graph-easy"));
184
185 std::ifstream ifs(outfilename.c_str());
186 std::string str((std::istreambuf_iterator<char>(ifs)),
187 std::istreambuf_iterator<char>());
188
189 if(provsql_verbose<20) {
190 if(unlink(outfilename.c_str())) {
191 throw CircuitException("Error removing "+outfilename);
192 }
193 std::string dirname=filename.substr(0, filename.rfind('/'));
194 if(rmdir(dirname.c_str())) {
195 throw CircuitException("Error removing temp directory "+dirname);
196 }
197 }
198
199 return str;
200}
gate_t
Strongly-typed gate identifier.
Definition Circuit.h:49
std::string to_string(gate_t g)
Convert a gate_t to its decimal string representation.
Definition Circuit.h:250
Provenance circuit variant that renders to GraphViz DOT format.
DotGate
Gate types for a DOT visualisation circuit.
Definition DotCircuit.h:42
@ PROJECT
Projection gate.
Definition DotCircuit.h:49
@ OPLUS
Semiring plus (⊕).
Definition DotCircuit.h:45
@ EQ
Equijoin gate.
Definition DotCircuit.h:50
@ OMINUS
Semiring monus (⊖), full.
Definition DotCircuit.h:46
@ OMINUSL
Monus, left child only.
Definition DotCircuit.h:48
@ OMINUSR
Monus, right child only.
Definition DotCircuit.h:47
@ DELTA
δ-semiring operator
Definition DotCircuit.h:52
@ IN
Input (variable) gate.
Definition DotCircuit.h:51
@ UNDETERMINED
Placeholder gate not yet assigned a type.
Definition DotCircuit.h:43
@ OTIMES
Semiring times (⊗).
Definition DotCircuit.h:44
Exception type thrown by circuit operations on invalid input.
Definition Circuit.h:206
std::string uuid
Definition Circuit.h:65
std::vector< gate_t > & getWires(gate_t g)
Definition Circuit.h:140
DotGate getGateType(gate_t g) const
Definition Circuit.h:130
virtual gate_t setGate(const uuid &u, gateType type)
Create or update the gate associated with UUID u.
Definition Circuit.hpp:73
std::vector< DotGate > gates
Definition Circuit.h:71
std::vector< std::vector< gate_t > > wires
Definition Circuit.h:72
virtual gate_t addGate()
Allocate a new gate with a default-initialised type.
Definition Circuit.hpp:56
virtual std::string toString(gate_t g) const override
Return a textual description of gate g for debugging.
gate_t addGate() override
Allocate a new gate with a default-initialised type.
std::set< gate_t > inputs
Input gate IDs (rendered as leaf nodes).
Definition DotCircuit.h:63
gate_t setGate(const uuid &u, DotGate type) override
Create or update the gate associated with UUID u.
std::string render() const
Render the entire circuit as a GraphViz DOT digraph string.
std::vector< std::string > desc
Per-gate label strings (indexed by gate ID).
Definition DotCircuit.h:64
int run_external_tool(const std::string &cmdline)
Run a shell command line, optionally extending PATH.
std::string format_external_tool_status(int rv, const std::string &tool)
Decode a system() return value into a human-readable message.
std::string find_external_tool(const std::string &name)
Locate an external tool by name.
Helpers for invoking external command-line tools.
int provsql_verbose
Verbosity level; controlled by the provsql.verbose_level GUC.
Definition provsql.c:78
Core types, constants, and utilities shared across ProvSQL.