Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
384 changes: 238 additions & 146 deletions src/evaluate_circuit.cpp

Large diffs are not rendered by default.

55 changes: 40 additions & 15 deletions src/evaluate_circuit.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,20 @@

namespace qflex {

struct QflexFinalQubits {
std::vector<std::vector<std::size_t>> qubits;
std::vector<std::size_t> output_pos_map;
std::vector<std::vector<std::size_t>> output_values_map;
};

/**
* Reads in grid layout from a file, which should be formatted as an I x J grid
* of zeroes (for "off" qubits) and ones (for "on" qubits).\
* @param grid_data std::istream* containing grid layout stored as a string.
* @param I std::size_t with the first spatial dimension of the grid of qubits.
* @param J std::size_t with the second spatial dimension of the grid of qubits.
* @return a list of coordinates for "off" qubits in the I x J grid provided.
* Returns final qubits when cuts are speficied for the terminal qubits.
* @param QflexGrid representing the circuit layout.
* @param ordering of the contraction.
* @return set of final qubits and their map.
*/
std::vector<std::vector<std::size_t>> read_grid_layout_from_stream(
std::istream* grid_data, std::size_t I, std::size_t J);
QflexFinalQubits get_final_qubits(
Comment thread
s-mandra marked this conversation as resolved.
const QflexGrid& grid, const std::list<ContractionOperation>& ordering);

/**
* Determines the final qubit positions and output states for a given ordering.
Expand All @@ -58,12 +62,33 @@ std::vector<std::vector<std::size_t>> read_grid_layout_from_stream(
* be populated by this method.
* @param output_states vector of output states for the given contraction
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update comment (output_states is no longer a parameter)

* ordering, to be populated by this method.
* @return the final state vector, with 'x' for cut locations.
* @return a vector of strings representing the final output states.
*/
std::vector<std::string> get_output_states(
const std::string& base_state, const QflexFinalQubits& final_qubits);

/**
* Given a grid of 3D tensors, rename indexes of the terminal tensors to
* reflect terminal qubits.
* @param QflexFinalQubits representing the final qubits.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For specifying parameters in function comments, please use the format:

* @param <parameter_name> <parameter_description>

Also, it looks like some parameters are missing from this comment.

* @param 3D grid of tensors.
*/
void apply_terminal_cuts(
Comment thread
s-mandra marked this conversation as resolved.
const QflexGrid& grid, const QflexFinalQubits& final_qubits,
std::vector<std::vector<std::vector<Tensor>>>* tensor_grid_3D_ptr);

/**
* Apply the final delta-tensors to the 3D grid of tensors.
* @param QflexGrid representing the circuit layout.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See previous comment.

* @param final state.
* @param 3D grid of tensors.
*/
std::string get_output_states(
const QflexInput* input, const std::list<ContractionOperation>& ordering,
std::vector<std::vector<std::size_t>>* final_qubits,
std::vector<std::string>* output_states);
void apply_delta_output(
Comment thread
s-mandra marked this conversation as resolved.
const QflexGrid& grid, const std::string& final_state,
const QflexFinalQubits& final_qubits,
const std::vector<std::vector<std::vector<Tensor>>>& tensor_grid_3D,
std::vector<std::vector<Tensor>>* tensor_grid_prt,
std::vector<s_type>* scratch_2D_ptr);

/**
* Evaluates a circuit and returns the final amplitudes of each state resulting
Expand All @@ -78,8 +103,8 @@ std::string get_output_states(
* States for qubits with terminal cuts are listed at the end of the state
* bitstring, in the order of their terminal cuts.
*/
std::vector<std::pair<std::string, std::complex<double>>> EvaluateCircuit(
QflexInput* input);
std::vector<std::tuple<std::string, std::string, std::complex<double>>>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this change, the @return spec is no longer accurate - please update.

EvaluateCircuit(const QflexInput& input);

} // namespace qflex

Expand Down
4 changes: 2 additions & 2 deletions src/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ struct QflexInput {
QflexOrdering ordering;
QflexCircuit circuit;
QflexGrid grid;
std::string initial_state;
std::string final_state;
std::vector<std::string> initial_states;
std::vector<std::string> final_states;
};

} // namespace qflex
Expand Down
94 changes: 64 additions & 30 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ tensor network, CPU-based simulator of large quantum circuits.
qflex::utils::readable_memory_string(qflex::global::memory_limit), R"(].
-t,--track-memory=<seconds> If <verbosity_level> > 0, track memory usage [default: )",
qflex::global::track_memory_seconds, R"(].
--initial-conf=<initial_conf> Initial configuration.
--final-conf=<final_conf> Final configuration.
--initial-conf=<initial_conf> Initial configuration [default: 00...00].
Comment thread
s-mandra marked this conversation as resolved.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This help message should indicate that multiple input/output strings are supported, and (briefly) describe how qFlex handles them.

--final-conf=<final_conf> Final configuration [default: 00...00].
--version Show version.
)");

Expand Down Expand Up @@ -72,17 +72,6 @@ int main(int argc, char** argv) {
qflex::global::track_memory_seconds =
args["<track_memory_seconds>"].asLong();

// Get initial/final configurations
if (static_cast<bool>(args["--initial-conf"]))
input.initial_state = args["--initial-conf"].asString();
else if (static_cast<bool>(args["<initial_conf>"]))
input.initial_state = args["<initial_conf>"].asString();

if (static_cast<bool>(args["--final-conf"]))
input.final_state = args["--final-conf"].asString();
else if (static_cast<bool>(args["<final_conf>"]))
input.final_state = args["<final_conf>"].asString();

// Getting filenames
std::string circuit_filename = static_cast<bool>(args["--circuit"])
? args["--circuit"].asString()
Expand All @@ -95,6 +84,63 @@ int main(int argc, char** argv) {
? args["--grid"].asString()
: args["<grid_filename>"].asString();

// Load circuit
input.circuit.load(std::ifstream(circuit_filename));

// Load ordering
input.ordering.load(std::ifstream(ordering_filename));

// Load grid
input.grid.load(grid_filename);

// Get initial/final configurations
for (const auto& arg : {"--initial-conf", "<initial_conf>"})
if (static_cast<bool>(args[arg])) {
std::stringstream ss(args[arg].asString());
std::string val;
while (std::getline(ss, val, ',')) {
if (val == "00...00")
input.initial_states.push_back(
std::string(input.circuit.num_active_qubits, '0'));
else if (val.find_first_not_of("01") != std::string::npos)
throw ERROR_MSG(
"Initial configurations can only have 0 or 1 characters.");
else if (std::size(val) != input.circuit.num_active_qubits)
throw ERROR_MSG(
"Initial configurations must have the number of active "
"qubits.");
else
input.initial_states.push_back(val);
}
}

for (const auto& arg : {"--final-conf", "<final_conf>"})
if (static_cast<bool>(args[arg])) {
std::stringstream ss(args[arg].asString());
std::string val;
while (std::getline(ss, val, ',')) {
if (val == "00...00")
input.final_states.push_back(
std::string(input.circuit.num_active_qubits, '0'));
else if (val.find_first_not_of("01") != std::string::npos)
throw ERROR_MSG(
"Final configurations can only have 0 or 1 characters.");
else if (std::size(val) != input.circuit.num_active_qubits)
throw ERROR_MSG(
"Final configurations must have the number of active qubits.");
else
input.final_states.push_back(val);
}
}

// Delete duplicate initial/final configuration
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd actually suggest that we return an error if duplicates are detected. The reasoning is this: if a user manually specifies two input states, they probably meant to provide two different input states. Since a simulation run can take a long time, we should fail early so the user can correct their mistake.

input.initial_states.erase(std::unique(std::begin(input.initial_states),
std::end(input.initial_states)),
std::end(input.initial_states));
input.final_states.erase(std::unique(std::begin(input.final_states),
std::end(input.final_states)),
std::end(input.final_states));

// Print OMP_NUM_THREADS and MKL_NUM_THREADS
if (qflex::global::verbose > 0)
for (const char* var : {"OMP_NUM_THREADS", "MKL_NUM_THREADS"})
Expand All @@ -114,19 +160,11 @@ int main(int argc, char** argv) {
alarm(qflex::global::track_memory_seconds);
}

// Load circuit
input.circuit.load(std::ifstream(circuit_filename));

// Load ordering
input.ordering.load(std::ifstream(ordering_filename));

// Load grid
input.grid.load(grid_filename);

// Evaluating circuit.
std::vector<std::pair<std::string, std::complex<double>>> amplitudes;
std::vector<std::tuple<std::string, std::string, std::complex<double>>>
amplitudes;
try {
amplitudes = qflex::EvaluateCircuit(&input);
amplitudes = qflex::EvaluateCircuit(input);
} catch (const std::string& err_msg) {
throw ERROR_MSG("Failed to call EvaluateCircuit(). Error:\n\t[", err_msg,
"]");
Expand All @@ -137,13 +175,9 @@ int main(int argc, char** argv) {
qflex::memory::print_memory_usage();

// Printing output.
for (std::size_t c = 0; c < amplitudes.size(); ++c) {
const auto& state = amplitudes[c].first;
const auto& amplitude = amplitudes[c].second;
std::cout << input.initial_state << " --> " << state << ": "
<< std::real(amplitude) << " " << std::imag(amplitude)
for (const auto& [initial_state, final_state, amplitude] : amplitudes)
std::cout << initial_state << " --> " << final_state << ": " << amplitude
<< std::endl;
}

} catch (const std::exception& ex) {
std::cerr << ex.what() << std::endl;
Expand Down
47 changes: 29 additions & 18 deletions src/pybind_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ std::vector<std::string> LoadStates(const py::dict &options,
states.push_back(in_state.cast<std::string>());
} else
throw ERROR_MSG("states must be strings.");
} else
states.push_back("");
}
return states;
}

Expand All @@ -84,27 +83,39 @@ std::vector<std::pair<std::string, std::complex<double>>> simulate(
LoadData(options, "grid", input.grid);
LoadData(options, "circuit", input.circuit);
LoadData(options, "ordering", input.ordering);
const auto initial_states = LoadStates(options, "initial");
const auto final_states = LoadStates(options, "final");

// Define container for amplitudes
std::vector<std::pair<
std::string, std::vector<std::pair<std::string, std::complex<double>>>>>
amplitudes;
// Load initial/final states
input.initial_states = [&options, &input]() {
Comment thread
s-mandra marked this conversation as resolved.
std::vector<std::string> states = LoadStates(options, "initial");
if (std::empty(states))
states.push_back(std::string(input.circuit.num_active_qubits, '0'));
return states;
}();
input.final_states = [&options, &input]() {
std::vector<std::string> states = LoadStates(options, "final");
if (std::empty(states))
states.push_back(std::string(input.circuit.num_active_qubits, '0'));
return states;
}();

// TODO: Pybind11 does not allow multiple types as output
if (std::size(initial_states) != 1 or std::size(final_states) != 1)
// TODO: Not yet supported in Cirq
if (std::size(input.initial_states) != 1 ||
Comment thread
s-mandra marked this conversation as resolved.
std::size(input.final_states) != 1)
throw ERROR_MSG("Not yet supported");

for (const auto &is : initial_states) {
for (const auto &fs : final_states) {
input.initial_state = is;
input.final_state = fs;
amplitudes.push_back({is, EvaluateCircuit(&input)});
}
}
// Remove duplicate initial/final states
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above - duplicate states should produce an error.

input.initial_states.erase(std::unique(std::begin(input.initial_states),
std::end(input.initial_states)),
std::end(input.initial_states));
input.final_states.erase(std::unique(std::begin(input.final_states),
std::end(input.final_states)),
std::end(input.final_states));

// Define container for amplitudes
std::vector<std::tuple<std::string, std::string, std::complex<double>>>
amplitudes = EvaluateCircuit(input);

return std::get<1>(amplitudes[0]);
return {{std::get<1>(amplitudes[0]), std::get<2>(amplitudes[0])}};

// Gently return an error msg if exception is known. Otherwise, rethrow
// exception.
Expand Down
Loading