Skip to content

Commit 111d102

Browse files
committed
[DAPHNE-#719] Add support for command-line arguments in DaphneLib
- Introduced two ways to pass DAPHNE arguments: array and string formats. - Updated documentation - Added testcases
1 parent 287f4c5 commit 111d102

File tree

8 files changed

+149
-21
lines changed

8 files changed

+149
-21
lines changed

doc/DaphneLib/Overview.md

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,12 +556,80 @@ tensor([[[100.5474, 100.9653],
556556
[100.3148, 100.3607]]], dtype=torch.float64)
557557
```
558558
559+
## Command-Line Arguments to Influence DAPHNE Behavior
560+
DAPHNE provides a range of command-line arguments that allow users to control its behavior and customize execution settings.
561+
These arguments can also be passed from DaphneLib.
562+
563+
*Example Array Format:*
564+
565+
```python
566+
from daphne.context.daphne_context import DaphneContext
567+
import numpy as np
568+
569+
m1 = np.array([1, 2, 3])
570+
dc = DaphneContext()
571+
X = dc.from_numpy(m1)
572+
X.print().compute(daphne_args=["--explain", "parsing_simplified, parsing", "--timing"])
573+
```
574+
575+
*Example String Format:*
576+
577+
```python
578+
from daphne.context.daphne_context import DaphneContext
579+
import numpy as np
580+
581+
m1 = np.array([1, 2, 3])
582+
dc = DaphneContext()
583+
X = dc.from_numpy(m1)
584+
X.print().compute(daphne_args="--explain=parsing_simplified,parsing --timing")
585+
```
586+
587+
*Output (memory addresses may vary):*
588+
589+
```
590+
IR after parsing:
591+
module {
592+
func.func @main() {
593+
%0 = "daphne.constant"() {value = 0 : si64} : () -> si64
594+
%1 = "daphne.constant"() {value = 43781056 : si64} : () -> si64
595+
%2 = "daphne.constant"() {value = 3 : si64} : () -> si64
596+
%3 = "daphne.constant"() {value = 1 : si64} : () -> si64
597+
%4 = "daphne.constant"() {value = 2 : si64} : () -> si64
598+
%5 = "daphne.cast"(%0) : (si64) -> ui32
599+
%6 = "daphne.cast"(%1) : (si64) -> ui32
600+
%7 = "daphne.receiveFromNumpy"(%5, %6, %2, %3) : (ui32, ui32, si64, si64) -> !daphne.Matrix<?x?xsi64>
601+
%8 = "daphne.constant"() {value = true} : () -> i1
602+
%9 = "daphne.constant"() {value = false} : () -> i1
603+
"daphne.print"(%7, %8, %9) : (!daphne.Matrix<?x?xsi64>, i1, i1) -> ()
604+
"daphne.return"() : () -> ()
605+
}
606+
}
607+
IR after parsing and some simplifications:
608+
module {
609+
func.func @main() {
610+
%0 = "daphne.constant"() {value = 43781056 : ui32} : () -> ui32
611+
%1 = "daphne.constant"() {value = 0 : ui32} : () -> ui32
612+
%2 = "daphne.constant"() {value = false} : () -> i1
613+
%3 = "daphne.constant"() {value = true} : () -> i1
614+
%4 = "daphne.constant"() {value = 3 : si64} : () -> si64
615+
%5 = "daphne.constant"() {value = 1 : si64} : () -> si64
616+
%6 = "daphne.receiveFromNumpy"(%1, %0, %4, %5) : (ui32, ui32, si64, si64) -> !daphne.Matrix<?x?xsi64>
617+
"daphne.print"(%6, %3, %2) : (!daphne.Matrix<?x?xsi64>, i1, i1) -> ()
618+
"daphne.return"() : () -> ()
619+
}
620+
}
621+
DenseMatrix(3x1, int64_t)
622+
1
623+
2
624+
3
625+
{"startup_seconds": 0.0238036, "parsing_seconds": 0.00178526, "compilation_seconds": 0.0734249, "execution_seconds": 0.0232295, "total_seconds": 0.122243}
626+
```
627+
559628
## Known Limitations
560629
561630
DaphneLib is still in an early development stage.
562631
Thus, there are a few limitations that users should be aware of.
563632
We plan to fix all of these limitations in the future.
564633
565-
- Using DAPHNE's command-line arguments to influence its behavior is not supported yet.
566634
- Some DaphneDSL built-in functions are not represented by DaphneLib methods yet.
567635
- High-level primitives for integrated data analysis pipelines, which are implemented in DaphneDSL, cannot be called from DaphneLib yet.

src/api/daphnelib/daphnelib.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,4 @@ extern "C" DaphneLibResult getResult() { return daphneLibRes; }
3131
* @brief Invokes DAPHNE with the specified DaphneDSL script and path to lib
3232
* dir.
3333
*/
34-
extern "C" int daphne(const char *libDirPath, const char *scriptPath) {
35-
const char *argv[] = {"daphne", "--libdir", libDirPath, scriptPath};
36-
int argc = 4;
37-
38-
return mainInternal(argc, argv, &daphneLibRes);
39-
}
34+
extern "C" int daphne(int argc, const char **argv) { return mainInternal(argc, argv, &daphneLibRes); }

src/api/python/daphne/operator/nodes/matrix.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ def getDType(self, d_type):
101101
def _is_numpy(self) -> bool:
102102
return self._np_array is not None
103103

104-
def compute(self, type="shared memory", verbose=False, asTensorFlow=False, asPyTorch=False, shape=None) -> Union[np.array]:
105-
return super().compute(type=type, verbose=verbose, asTensorFlow=asTensorFlow, asPyTorch=asPyTorch, shape=shape)
104+
def compute(self, type="shared memory", verbose=False, asTensorFlow=False, asPyTorch=False, shape=None, daphne_args=None) -> Union[np.array]:
105+
return super().compute(type=type, verbose=verbose, asTensorFlow=asTensorFlow, asPyTorch=asPyTorch, shape=shape, daphne_args=daphne_args)
106106

107107
def __add__(self, other: VALID_ARITHMETIC_TYPES) -> 'Matrix':
108108
return Matrix(self.daphne_context, '+', [self, other])

src/api/python/daphne/operator/operation_node.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def update_node_in_input_list(self, new_node, current_node):
9191
current_index = self._unnamed_input_nodes.index(current_node)
9292
self._unnamed_input_nodes[current_index] = new_node
9393

94-
def compute(self, type="shared memory", verbose=False, asTensorFlow=False, asPyTorch=False, shape=None, useIndexColumn=False):
94+
def compute(self, type="shared memory", verbose=False, asTensorFlow=False, asPyTorch=False, shape=None, useIndexColumn=False, daphne_args=None):
9595
"""
9696
Compute function for processing the Daphne Object or operation node and returning the results.
9797
The function builds a DaphneDSL script from the node and its context, executes it, and processes the results
@@ -103,6 +103,7 @@ def compute(self, type="shared memory", verbose=False, asTensorFlow=False, asPyT
103103
:param asPyTorch: If True and the result is a matrix, the output will be converted to a PyTorch tensor.
104104
:param shape: If provided and the result is a matrix, it defines the shape to reshape the resulting tensor (either TensorFlow or PyTorch).
105105
:param useIndexColumn: If True and the result is a DataFrame, uses the column named "index" as the DataFrame's index.
106+
:param daphne_args: Optional arguments specifically for Daphne DSL script execution, which can customize runtime behavior.
106107
107108
:return: Depending on the parameters and the operation's output type, this function can return:
108109
- A pandas DataFrame for frame outputs.
@@ -123,7 +124,7 @@ def compute(self, type="shared memory", verbose=False, asTensorFlow=False, asPyT
123124
if verbose:
124125
exec_start_time = time.time()
125126

126-
self._script.execute()
127+
self._script.execute(daphne_args)
127128
self._script.clear(self)
128129

129130
if verbose:

src/api/python/daphne/script_building/script.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import ctypes
2929
import os
30+
import shlex
3031
from typing import List, Dict, TYPE_CHECKING
3132

3233
if TYPE_CHECKING:
@@ -88,23 +89,43 @@ def clear(self, dag_root:DAGNode):
8889
self._dfs_clear_dag_nodes(dag_root)
8990
self._variable_counter = 0
9091

91-
def execute(self):
92+
def execute(self, daphne_args):
9293
temp_out_path = os.path.join(TMP_PATH, "tmpdaphne.daphne")
93-
temp_out_file = open(temp_out_path, "w")
94-
temp_out_file.writelines(self.daphnedsl_script)
95-
temp_out_file.close()
96-
97-
#os.environ['OPENBLAS_NUM_THREADS'] = '1'
98-
res = DaphneLib.daphne(ctypes.c_char_p(str.encode(PROTOTYPE_PATH)), ctypes.c_char_p(str.encode(temp_out_path)))
94+
with open(temp_out_path, "w") as temp_out_file:
95+
temp_out_file.writelines(self.daphnedsl_script)
96+
97+
# Construct argv
98+
argv = ["daphne", "--libdir", PROTOTYPE_PATH]
99+
100+
# Handle daphne_args
101+
if isinstance(daphne_args, str):
102+
argv.extend(shlex.split(daphne_args))
103+
elif isinstance(daphne_args, list):
104+
argv.extend(daphne_args)
105+
elif daphne_args is None:
106+
pass
107+
else:
108+
raise TypeError("daphne_args must be a string or a list")
109+
110+
# Add the script path
111+
argv.append(temp_out_path)
112+
113+
# Convert argv to ctypes
114+
argc = len(argv)
115+
argv_ctypes = (ctypes.c_char_p * argc)(* [arg.encode('utf-8') for arg in argv])
116+
117+
# Set argument and return types for the C function
118+
DaphneLib.daphne.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)]
119+
DaphneLib.daphne.restype = ctypes.c_int
120+
121+
res = DaphneLib.daphne(ctypes.c_int(argc), argv_ctypes)
99122
if res != 0:
100123
# Error message with DSL code line.
101124
error_message = DaphneLib.getResult().error_message.decode("utf-8")
125+
raise RuntimeError(f"Error in DaphneDSL script: {error_message}")
102126
# Remove DSL code line from error message.
103127
# index_code_line = error_message.find("Source file ->") - 29
104128
# error_message = error_message[:index_code_line]
105-
106-
raise RuntimeError(f"Error in DaphneDSL script: {error_message}")
107-
#os.environ['OPENBLAS_NUM_THREADS'] = '32'
108129

109130
def _dfs_dag_nodes(self, dag_node: VALID_INPUT_TYPES)->str:
110131
"""Uses Depth-First-Search to create code from DAG

test/api/python/DaphneLibTest.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
#include <cstdlib>
2727
#include <cstring>
2828

29+
#include <fstream>
30+
#include <sstream>
31+
2932
const std::string dirPath = "test/api/python/";
3033

3134
#define MAKE_TEST_CASE(name) \
@@ -65,6 +68,19 @@ const std::string dirPath = "test/api/python/";
6568
const std::string prefix = dirPath + name; \
6669
compareDaphneLibToStr(str, prefix + ".py"); \
6770
}
71+
#define MAKE_TEST_CASE_FILE(name) \
72+
TEST_CASE(name ".py", TAG_DAPHNELIB) { \
73+
const std::string prefix = dirPath + name; \
74+
std::ifstream fileStream(prefix + ".txt"); \
75+
std::stringstream buffer; \
76+
buffer << fileStream.rdbuf(); \
77+
std::stringstream out; \
78+
std::stringstream err; \
79+
int status = runDaphneLib(out, err, (prefix + ".py").c_str()); \
80+
CHECK(status == StatusCode::SUCCESS); \
81+
CHECK(err.str() != ""); \
82+
CHECK(out.str() == buffer.str()); \
83+
}
6884

6985
MAKE_TEST_CASE("data_transfer_numpy_1")
7086
MAKE_TEST_CASE("data_transfer_numpy_2")
@@ -158,3 +174,4 @@ MAKE_TEST_CASE("user_def_func_3_inputs")
158174
// MAKE_TEST_CASE_PARAMETRIZED("user_def_func_with_condition", "param=3.8")
159175
// MAKE_TEST_CASE("user_def_func_with_for_loop")
160176
// MAKE_TEST_CASE("user_def_func_with_while_loop")
177+
MAKE_TEST_CASE_FILE("daphne_args")

test/api/python/daphne_args.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import numpy as np
2+
from daphne.context.daphne_context import DaphneContext
3+
4+
m1 = np.array([3, 9, 12])
5+
dc = DaphneContext()
6+
X = dc.from_numpy(m1)
7+
X.print().compute(daphne_args=["--vec", "--pin-workers"])
8+
X.print().compute(daphne_args=["--explain", "parsing_simplified"])
9+
X.print().compute(daphne_args=["--explain", "parsing_simplified,parsing", "--timing"])
10+
X.print().compute(daphne_args="--explain=parsing_simplified,parsing --timing")

test/api/python/daphne_args.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
DenseMatrix(3x1, int64_t)
2+
3
3+
9
4+
12
5+
DenseMatrix(3x1, int64_t)
6+
3
7+
9
8+
12
9+
DenseMatrix(3x1, int64_t)
10+
3
11+
9
12+
12
13+
DenseMatrix(3x1, int64_t)
14+
3
15+
9
16+
12

0 commit comments

Comments
 (0)