DataList
¶
Recursion over QList
values imposes a qubit-based
view of quantum programming. Many domain-specific quantum algorithms
require a higher level of abstraction. For this purpose,
FLEQ introduces the datalist::DataList
type for compile-time strings.
Like a QList
, a DataList
is an immutable compile-time list that wraps statically-defined C strings or
char
arrays. A DataList
can be constructed from a string literal via
the constructor,
or it can be constructed from a file using a pair of macros.
// in file "text.txt" ////////////////////////
STRINGIFYDATA(
This is from a file!
)
//////////////////////////////////////////////
const datalist::DataList str_src("This is from source!");
import_with_name_begin(file_str)
#include "test.txt"
import_with_name_end(file_str);
// equivalent to
//
// char file_str_raw[] = "this is from file!";
// const DataList file_str(file_str_raw);
Basic DataList
operations¶
Like QList
and ordinary C++ std::string
, a DataList
can be
sliced, concatenated, sized, and addressed.
unsigned int datalist::DataList::size()
Return the length of the
DataList
. Compile-time resolvable.
char datalist::DataList::operator[](unsigned long i)
Index into a
DataList
, e.g.data[i]
.
datalist::DataList datalist::operator+(DataList data1, DataList data2)
Concatenate two data lists, e.g.
data1 + data2
.
datalist::DataList datalist::DataList::operator()(unsigned long start, unsigned long end)
datalist::DataList datalist::DataList::operator()(DataList start, unsigned long end)
datalist::DataList datalist::DataList::operator()(unsigned long start, DataList end)
datalist::DataList datalist::DataList::operator()(DataList start, DataList end)
Each of the four variants above returns a slice of a
DataList
, starting at the indexstart
(inclusive) and ending at the indexend
(exclusive); e.g.data(start, end)
. Whenstart
orend
areDataList
values, the index associated with thatDataList
is the result offind(start)
/find(end)
respectively.
datalist::DataList datalist::operator>>(DataList data, unsigned long i)
Return the right shift of
data
by offseti
, i.e.data >> i == data(i, data.size())
.
datalist::DataList datalist::operator<<(DataList data, unsigned long i)
Return the left shift of
data
by offseti
, i.e.data << i == data(0, data.size() - i)
.
datalist::DataList datalist::operator+(DataList data, unsigned long i)
Return the right shift of
data
by offseti
, i.e.data + i == data(i, data.size())
.
datalist::DataList datalist::operator++()
Return the right shift of a
DataList
by 1, i.e.++data == data(1, data.size())
.
unsigned long datalist::DataList::count(DataList sub_str1, DataList sub_str2, ...)
Return the number of occurrences of any of the substring arguments in the current
DataList
.In the case of variadic arguments like
count
, the behavior isOR
-ed over all the arguments. So for example,data.count("A", "B", "C")
returns the number of times theDataList
contains"A"
or"B"
or"C"
.
DataList
conversions¶
FLEQ provides several functions for casting a DataList
of a
specific form to basic C types and visa versa:
int datalist::DataList::to_int()
and int datalist::_i(DataList data)
Convert a
DataList
into an integer; analogous tostd::stoi
.
double datalist::DataList::to_double()
and double datalist::_d(DataList data)
Convert a
DataList
into a double; analogous tostd::stod
.
bool datalist::DataList::to_bool()
and bool datalist::_b(DataList data)
Convert a
DataList
into an bool.
datalist::DataList(int i)
Produce a
DatatList
from an integer, e.g.DataList x (5);
produces aDataList
variablex
with value"5"
.
If a cast fails (i.e. if the to_int()
is called on a DataList
that does
not consist of digits), the behavior is undefined, which could lead to incorrect
results. Note that the compiler does not exit in this case, though this behavior
may be changed in future versions. Best practice is to confirm the DataList
has the appropriate form before casting. For example, the following QExpr
function expects a DataList
of the form 0
or 1
, and casts the result
to a boolean used in the conditional of a cIf
.
QExpr unguarded_cast(qbit &q, datalist::DataList cond) {
return qexpr::cIf(cond.to_bool(), qexpr::_X(q), qexpr::identity());
}
If this function is evaluated with an ill-formed input, such as
unguarded_cast(q, "X")
, the program will still compile, but will fail at
runtime without any kind of error handling.
A user can prevent this failure case by using an additional cIf
to ensure
the DataList
has the appropriate form. For example:
QExpr guarded_cast(qbit &q, datalist::DataList cond) {
return qexpr::cIf(cond == datalist::DataList("0")
|| cond == datalist::DataList("1"),
qexpr::cIf(cond.to_bool(), qexpr::_X(q), qexpr::identity()),
qexpr::exitAtCompile("Expected 0 or 1.")
);
}
If this function is evaluated with an ill-formed input, the compiler will exit with the error message “Expected 0 or 1”.
Parsing¶
To aid with parsing, DataList
includes several substring search functions
returning an index into the current DataList
.
unsigned long datalist::DataList::find(DataList sub_str)
Returns the index of the start of the first occurrence of
sub_str
in theDataList
.
unsigned long datalist::DataList::find_last(DataList sub_str)
Returns the index of the start of the last occurrence of
sub_str
in theDataList
.
unsigned long datalist::DataList::find_any(DataList chars)
Returns the index of the first occurrence of any of the characters in
chars
. For example,DataList("xyz20").find_any("012")
will return the index3
as"xyz20"[3] = "2"
.
unsigned long datalist::DataList::find_any_last(DataList chars)
Returns the index of the last occurrence of any of the characters in
chars
.
unsigned long datalist::DataList::find_not(DataList sub_str)
Returns the index of the first character not matching any character of
sub_str
. Will return0
ifsub_str
is not a prefix of the currentDataList
.
unsigned long datalist::DataList::find_not_last(DataList sub_str)
Returns the index of the last character not matching any character of
sub_str
. Will returnsize()
ifsub_str
is not a suffix of the currentDataList
.
In addition, DataList
contains several utilities that return substrings.
datalist::DataList datalist::DataList::next(DataList sub_str1, DataList sub_str2, ...)
Returns the
DataList
slice beginning at the first occurrence of any of the arguments. For example:
DataList("find this 123 or this 345").next("this") = "this 123 or this 345"
DataList("The quick brown fox.").next("fox", "quick") = "quick brown fox."
DataList("The quick brown fox.").next("fox", "house") = "fox."
datalist::DataList datalist::DataList::after_next
(DataList sub_str1,
DataList sub_str2,
...)
Returns the
DataList
slice beginning directly after the first occurrence of any of the arguments. For example:
DataList("find this 123 or this 345").after_next("this") = " 123 or this 345"
DataList("The quick brown fox.").after_next("fox", "quick") = " brown fox."
DataList("The quick brown fox.").after_next("fox", "house") = "."
datalist::DataList datalist::DataList::next_not(DataList sub_str)
Returns the
DataList
occurring after the indexfind_not(sub_str)
.
datalist::DataList datalist::DataList::next_block(DataList chars)
Returns the first
DataList
slice whose elements all match any of the characters inchars
.For example,
d.next_block("0123456789")
will return the first integer ind
.
DataList("July 18, 1968").next_block("0123456789") = "18"
datalist::DataList datalist::DataList::last(DataList sub_str1,
DataList sub_str2,
...)
Returns the
DataList
slice beginning at the last occurrence of any of the substring arguments.
datalist::DataList datalist::DataList::after_last(DataList sub_str1,
DataList sub_str2,
...)
Returns the
DataList
slice beginning immediately after the last occurrence of any of the substring arguments.
datalist::DataList datalist::DataList::last_not(DataList sub_str)
Returns the slice of the current
DataList
starting at the indexfind_not_last(sub_str)
.
datalist::DataList datalist::DataList::last_block(DataList sub_str)
Returns the last
DataList
block whose elements all match any of the characters inchars
. For example:
DataList("July 18, 1968").last_block("0123456789") = "1968"
Runtime and compile-time conversions¶
If a compile-time DataList
is needed at runtime, it can be converted into
either a runtime equivalent std::string
, or to a char
array.
std::string datalist::to_string(DataList data)
Return the runtime C++ string represented by the
DataList
data
.
char * datalist::to_char_array(DataList data)
.
Return the runtime C string represented by the
DataList
data
.
Some DSL examples (see Domain-specific languages using FLEQ) may want to create an array or QList
whose size is specified by a DataList
input. The datalist
namespace thus
also includes a template function that can take as input a constant integer and allocates
an array of that size of any type as specified by the template argument, including qbit
.
template<typename Type> Type
* datalist::IQC_alloca(DataList name = "",
const unsigned long N = 1)
At compile-time, generate an array of
Type
objects of sizeN
with IR namename
(for printing and debugging purposes) and return a pointer to the first element. Note, no additional memory management is required. The scope of the variable will be the same as if a standard array allocation.
For example, suppose a user has a quantum kernel expression that takes as input a QList
of arbitrary size and returns (by reference) a boolean result. The user want to write a function that takes a DataList
argument that specifies the number of qubits to use in the QList
. They could use IQC_alloca
to allocate the appropriate QList
at compile-time as follows:
QExpr op(qlist::QList qs, bool& result) {...}
QExpr opOnNQubits(datalist::DataList N, bool &result) {
qbit *qs_raw = datalist::IQC_alloca<qbit>("qs", N.to_int());
qlist::QList qs (qs_raw);
return op(qs, result);
}
This function can be invoked on 3 qubits via a call to qexpr::eval_hold(opOnNQubits("3", result))
.