Commit 68e2a3bf authored by Mccaskey, Alex's avatar Mccaskey, Alex
Browse files

Started work on IR version 3. Goal here is to make it easier to connect...


Started work on IR version 3. Goal here is to make it easier to connect classical C++ function arguments to runtime-expansion of the IR.
Signed-off-by: Mccaskey, Alex's avatarAlex McCaskey <mccaskeyaj@ornl.gov>
parent 3103795b
@startuml
class Instruction {
+ addArgument(arg : CompositeArgument, idx_for_param : int)
+ applyRuntimeArguments()
}
class CompositeInstruction {
+ getArgument(arg : string) : CompositeArgument
+ updateRuntimeArguments(values : VariadicValues...)
}
class CompositeArgument {
name : string
type : string
runtimeValue : HeterogeneousMap
}
class Gate {
+ applyRuntimeArguments()
}
class Circuit {
+ applyRuntimeArguments()
}
class Exp {
+ applyRuntimeArguments()
}
Instruction <|.. CompositeInstruction
Instruction "*" <-- "1" CompositeInstruction
CompositeArgument "*" <-- "1" CompositeInstruction
Instruction <|.. Gate
CompositeInstruction <|.. Circuit
Circuit <|.. Exp
note right of Instruction::addArgument
For concrete Instruction, add the CompositeArgument
to some internal map with idx as the index of the
corresponding InstructionParameter
For CompositeInstruction, just add this CompositeArgument
to a vector (maybe)
end note
note right of Gate::applyRuntimeArguments
Simple implementation here is for all rotation gates
for (auto& kv : arguments_map) {
parameters[kv.second] = kv.first->runtimeValue.get<double>(INTERNAL_KEY);
}
end note
note left of Instruction
Instructions keep map of pointers to CompositeArguments
from parent to index of corresponding Instruction Parameter,
e.g.
foo(double x, double y, double z) {
Rx(x)
Ry(y)
U(x,y,z)
}
foo Composite Instruction has 2 CompositeArguments
first Rx Instruction keeps map from one with
name x to idx == 0, second Ry keeps map of y to
idx == 0. U keeps map {x:0, y:1, z:2}.
This can
be easily supplied by Antlr parsers.
end note
note left of Instruction::applyRuntimeArguments
On concrete Instructions, this updates InstructionParameters
with concrete values in the Instructions argument map.
For CompositeInstructions, this is called on all its
sub-Instructions ( the rest of the tree ).
Updates on concrete Instructions could look like
my_args[idx_of_param]->runtimeValue.get<double>("__xacc_value_key"),
the concrete values (runtimeValue) comes from
a call to the variadic CompositeInstruction::updateRuntimeArguments
end note
note left of CompositeInstruction::updateRuntimeArguments
Sets Variadic values to each runtimeValue HeterogeneousMap with some
internal unique key like "__xacc_value_key".
end note
note right of Exp::applyRuntimeArguments
This guys is free to reference any arguments given
to the parent CompositeInstruction. For example,
an Observable
end note
@enduml
\ No newline at end of file
......@@ -68,6 +68,10 @@ public:
AnnealingProgram(std::string kernelName, std::vector<std::string> p)
: _name(kernelName), variables(p) {}
void applyRuntimeArguments() override {
}
std::shared_ptr<CompositeInstruction> enabledView() override {
auto newF = std::make_shared<AnnealingProgram>(_name, variables);
for (int i = 0; i < nInstructions(); i++) {
......
......@@ -44,6 +44,14 @@ public:
const std::string name() const override { return "dwqmi"; }
const std::string description() const override { return ""; }
void addArgument(std::shared_ptr<CompositeArgument> arg,
const int idx_of_inst_param) override {
}
void applyRuntimeArguments() override {
}
void mapBits(std::vector<std::size_t> bitMap) override {}
const int nRequiredBits() const override {return 2;}
void setBits(const std::vector<std::size_t> bits) override {
......
......@@ -90,6 +90,12 @@ public:
circuitName = name;
}
void applyRuntimeArguments() override {
for (auto& i : instructions) {
i->applyRuntimeArguments();
}
}
const std::string getTag() override {return "";}
void setTag(const std::string& tag) override {return;}
......
......@@ -193,7 +193,7 @@ public:
: Gate("U", std::vector<InstructionParameter>{
InstructionParameter(0.0), InstructionParameter(0.0),
InstructionParameter(0.0)}) {}
U(std::size_t qbit, std::vector<xacc::InstructionParameter> params) : Gate("U", params){}
U(std::size_t qbit, double theta, double phi, double lambda)
: Gate("U", std::vector<std::size_t>{qbit},
std::vector<InstructionParameter>{InstructionParameter(theta),
......
......@@ -33,6 +33,8 @@ protected:
std::map<std::size_t, std::string> bitIdxExpressions;
std::map<std::shared_ptr<CompositeArgument>, int> arguments;
public:
Gate();
Gate(std::string name);
......@@ -48,13 +50,31 @@ public:
const std::string toString() override;
void addArgument(std::shared_ptr<CompositeArgument> arg,
const int idx_of_inst_param) override {
arguments.insert({arg, idx_of_inst_param});
}
void applyRuntimeArguments() override {
for (auto &kv : arguments) {
parameters[kv.second] =
kv.first->runtimeValue.get<double>(INTERNAL_ARGUMENT_VALUE_KEY);
}
}
const std::vector<std::size_t> bits() override;
void setBits(const std::vector<std::size_t> bits) override { qbits = bits; }
std::string getBufferName(const std::size_t bitIdx) override;
void setBufferNames(const std::vector<std::string> bufferNamesPerIdx) override;
std::vector<std::string> getBufferNames() override {return buffer_names;}
void setBitExpression(const std::size_t bit_idx, const std::string expr) override {bitIdxExpressions.insert({bit_idx,expr});}
std::string getBitExpression(const std::size_t bit_idx) override {return bitIdxExpressions[bit_idx];}
void
setBufferNames(const std::vector<std::string> bufferNamesPerIdx) override;
std::vector<std::string> getBufferNames() override { return buffer_names; }
void setBitExpression(const std::size_t bit_idx,
const std::string expr) override {
bitIdxExpressions.insert({bit_idx, expr});
}
std::string getBitExpression(const std::size_t bit_idx) override {
return bitIdxExpressions[bit_idx];
}
const InstructionParameter getParameter(const std::size_t idx) const override;
std::vector<InstructionParameter> getParameters() override;
......
......@@ -44,6 +44,14 @@ public:
Pulse(const Pulse &inst);
void addArgument(std::shared_ptr<CompositeArgument> arg, const int idx_for_param) override {
}
void applyRuntimeArguments() override {
// by default do nothing
}
std::string channel() override { return ch; }
void setChannel(const std::string c) override { ch = c; }
std::size_t start() override { return t0; }
......
......@@ -17,11 +17,59 @@
#include "xacc.hpp"
using namespace xacc::quantum;
TEST(GateTester, checkIR3) {
/* mimic the following
*
* __qpu__ foo (qbit q, double x, double y, double z) {
* Rx(q[0], x);
* U(q[0], x, y, z);
* }
*
*/
auto circuit = std::make_shared<Circuit>("foo");
circuit->addVariable("x");
circuit->addVariable("y");
circuit->addVariable("z");
circuit->addArgument("x", "double");
circuit->addArgument("y", "double");
circuit->addArgument("z", "double");
auto argx = circuit->getArgument("x");
auto argy = circuit->getArgument("y");
auto argz = circuit->getArgument("z");
auto rx = std::make_shared<Rx>(0, "x");
rx->addArgument(argx, 0);
auto u = std::make_shared<U>(0, std::vector<xacc::InstructionParameter>{"x", "y", "z"});
u->addArgument(argx, 0);
u->addArgument(argy, 1);
u->addArgument(argz, 2);
circuit->addInstructions({rx,u});
EXPECT_THROW(circuit->updateRuntimeArguments(2.2), std::runtime_error);
circuit->updateRuntimeArguments(2.2, 3.3, 4.4);
circuit->applyRuntimeArguments();
std::cout << circuit->toString() <<"\n";
circuit->updateRuntimeArguments(3.2, 4.3, 5.3);
circuit->applyRuntimeArguments();
std::cout << circuit->toString() <<"\n";
}
TEST(GateTester, checkBugBug) {
auto cnot = std::make_shared<CNOT>(0,1);
cnot->setBufferNames({"a","b"});
std::cout << "CNOT:\n" << cnot->toString() << "\n";
}
TEST(GateTester, checkBasicGatesAndCircuits) {
auto h = std::make_shared<Hadamard>(0);
......
......@@ -77,5 +77,5 @@ find_package(UUID)
if (UUID_FOUND AND EXISTS $ENV{HOME}/.zmq/include/msgpack.hpp)
message(
STATUS "${BoldGreen}Building QCS Accelerator.${ColorReset}")
add_subdirectory(qcs)
#add_subdirectory(qcs)
endif()
......@@ -13,9 +13,11 @@ xaccsrc
xacclambda : '['('&'|'=')?']' '(' typedparam (',' typedparam) * ')' '{' mainprog? '}';
typedparam : type id;
type : 'qbit' | 'qreg' | 'int' | 'double' | 'float' | 'std::vector<double>';
typedparam : type ('&' | '*')? varname;
var_name : id;
type : id | id'::'id ;
/*'qbit' | 'qreg' | 'int' | 'double' | 'float' | 'std::vector<double>';*
/***********************************************************************/
......
......@@ -25,11 +25,12 @@ namespace xacc {
using InstPtr = std::shared_ptr<Instruction>;
// CompositeInstructions are Instructions that contain further Instructions (which
// of course can be other CompositeInstructions). This forms the familiar tree, or
// composite pattern, where nodes are CompositeInstructions and leaves are concrete
// Instruction instances. CompositeInstructions are also Persistable, meaning a
// CompositeInstruction can be persisted (read) from an output (input) stream.
// CompositeInstructions are Instructions that contain further Instructions
// (which of course can be other CompositeInstructions). This forms the familiar
// tree, or composite pattern, where nodes are CompositeInstructions and leaves
// are concrete Instruction instances. CompositeInstructions are also
// Persistable, meaning a CompositeInstruction can be persisted (read) from an
// output (input) stream.
// Adding Instructions:
// Implementations of CompositeInstruction are responsible for providing
......@@ -44,15 +45,15 @@ using InstPtr = std::shared_ptr<Instruction>;
// terminating nodes in Instruction tree, and represent composite
// Instructions that can not be known at compile time, but are
// parameterized by a map of options. Clients can query if the
// CompositeInstruction hasChildren(), and if not, expand the CompositeInstruction to a
// list of its constituent children at runtime given a valid
// set of options. i.e., a CompositeInstruction that can generate a UCCSD circuit
// for gate model quantum computing needs to know the number of
// qubits and number of electrons, which may be unknown at CompositeInstruction
// build time. Later, clients can invoke f->expand({{"ne",2},{"nq",4}})
// to expand this CompositeInstruction and make it a composite node instead of a
// terminating node. In this way, developers can contribute dynamic
// runtime parameterized Instructions.
// CompositeInstruction hasChildren(), and if not, expand the
// CompositeInstruction to a list of its constituent children at runtime given a
// valid set of options. i.e., a CompositeInstruction that can generate a UCCSD
// circuit for gate model quantum computing needs to know the number of qubits
// and number of electrons, which may be unknown at CompositeInstruction build
// time. Later, clients can invoke f->expand({{"ne",2},{"nq",4}}) to expand this
// CompositeInstruction and make it a composite node instead of a terminating
// node. In this way, developers can contribute dynamic runtime parameterized
// Instructions.
// Note, expanding a CompositeInstruction node should expand all sub-nodes.
......@@ -69,7 +70,81 @@ using InstPtr = std::shared_ptr<Instruction>;
// we also expose a coefficient complex value on the CompositeInstruction.
class CompositeInstruction : public Instruction, public Persistable {
protected:
int _internal_counter = 0;
int _count_args_counter = 0;
std::vector<std::shared_ptr<CompositeArgument>> arguments;
// Take the concrete value and set it on the
// correct argument.
template <typename T> void updateArgs(T &value) {
if (arguments[_internal_counter]->runtimeValue.keyExists<T>(
INTERNAL_ARGUMENT_VALUE_KEY)) {
arguments[_internal_counter]->runtimeValue.get_mutable<T>(
INTERNAL_ARGUMENT_VALUE_KEY) = value;
} else {
arguments[_internal_counter]->runtimeValue.insert(
INTERNAL_ARGUMENT_VALUE_KEY, value);
}
_internal_counter++;
}
// Recursively unpack the varidadic parameters and
// call updateArgs on each one.
void constructArgs() { return; }
template <typename First, typename... Rest>
void constructArgs(First firstArg, Rest... rest) {
updateArgs(firstArg);
constructArgs(rest...);
_internal_counter = 0;
}
void countArgs() {}
template <typename First, typename... Rest>
void countArgs(First firstArg, Rest... rest) {
_count_args_counter++;
countArgs(rest...);
}
public:
template <typename... RuntimeArgs>
void updateRuntimeArguments(RuntimeArgs... args) {
countArgs(args...);
if (_count_args_counter != arguments.size()) {
int tmp = _count_args_counter;
_count_args_counter = 0;
throw std::runtime_error(
"[xacc error] Invalid number of runtime arguments for " + name() + ", " + std::to_string(tmp));
}
_count_args_counter = 0;
constructArgs(args...);
}
void addArgument(std::shared_ptr<CompositeArgument> arg,
const int idx_of_inst_param) override {
arguments.push_back(arg);
}
virtual void addArgument(const std::string arg_name,
const std::string arg_type) {
arguments.emplace_back(new CompositeArgument(arg_name, arg_type));
}
virtual std::shared_ptr<CompositeArgument>
getArgument(const std::string &name) {
for (auto &arg : arguments) {
if (arg->name == name) {
return arg;
}
}
return nullptr;
}
// To be implemented by sub-types, goal is to walk
// the Instruction tree and call Instruction::applyRuntimeArguments
// giving the concrete Instruction an opportunity to update
// virtual void applyRuntimeArguments() = 0;
// This should return the number
// of concrete leaves in the tree
......@@ -89,19 +164,24 @@ public:
virtual void clear() = 0;
virtual void addInstruction(InstPtr instruction) = 0;
virtual void addInstructions(std::vector<InstPtr>& instruction) = 0;
virtual void addInstructions(const std::vector<InstPtr>& instruction) = 0;
virtual void addInstructions(const std::vector<InstPtr> &&insts) {addInstructions(insts);}
virtual void addInstructions(std::vector<InstPtr> &instruction) = 0;
virtual void addInstructions(const std::vector<InstPtr> &instruction) = 0;
virtual void addInstructions(const std::vector<InstPtr> &&insts) {
addInstructions(insts);
}
virtual bool hasChildren() const = 0;
virtual bool expand(const HeterogeneousMap& runtimeOptions) = 0;
virtual bool expand(const HeterogeneousMap &runtimeOptions) = 0;
virtual const std::vector<std::string> requiredKeys() = 0;
virtual void addVariable(const std::string variableName) = 0;
virtual void addVariables(const std::vector<std::string>& variables) = 0;
virtual void addVariables(const std::vector<std::string>&& variables) {addVariables(variables);}
virtual void addVariables(const std::vector<std::string> &variables) = 0;
virtual void addVariables(const std::vector<std::string> &&variables) {
addVariables(variables);
}
virtual const std::vector<std::string> getVariables() = 0;
virtual void replaceVariable(const std::string variable, const std::string newVariable) = 0;
virtual void replaceVariable(const std::string variable,
const std::string newVariable) = 0;
virtual const std::size_t nVariables() = 0;
virtual const int depth() = 0;
......@@ -127,10 +207,12 @@ public:
virtual void set_accelerator_signature(const std::string signature) = 0;
virtual const std::string getTag() = 0;
virtual void setTag(const std::string& tag) = 0;
virtual void setTag(const std::string &tag) = 0;
virtual ~CompositeInstruction() {}
};
template CompositeInstruction* HeterogeneousMap::getPointerLike<CompositeInstruction>(const std::string key) const;
template CompositeInstruction *
HeterogeneousMap::getPointerLike<CompositeInstruction>(
const std::string key) const;
} // namespace xacc
#endif
......@@ -19,12 +19,14 @@
#include "heterogeneous.hpp"
namespace xacc {
using InstructionParameter = Variant<int,double,std::string>;
using InstructionParameter = Variant<int, double, std::string>;
// Util func to get a double parameter from an InstructionParameter variant.
// Note: most of the cases, we have double-type parameters (e.g. rotation angles).
// This also handles int->double and string->double conversion if necessary.
static double InstructionParameterToDouble(const xacc::InstructionParameter& in_parameter) {
// Note: most of the cases, we have double-type parameters (e.g. rotation
// angles). This also handles int->double and string->double conversion if
// necessary.
static double
InstructionParameterToDouble(const xacc::InstructionParameter &in_parameter) {
if (in_parameter.which() == 0) {
// Convert int to double
return static_cast<double>(in_parameter.as<int>());
......@@ -32,11 +34,14 @@ static double InstructionParameterToDouble(const xacc::InstructionParameter& in_
return in_parameter.as<double>();
} else {
// Check if this string parameter can be converted to a double
const auto isNumber = [](const std::string& in_string, double& out_double) -> bool {
char* end = 0;
const auto isNumber = [](const std::string &in_string,
double &out_double) -> bool {
char *end = 0;
double val = strtod(in_string.c_str(), &end);
// This whole string must be a valid double number
const bool isConversionOkay = (end != in_string.c_str()) && (*end == '\0') && (val != std::numeric_limits<double>::infinity());
const bool isConversionOkay =
(end != in_string.c_str()) && (*end == '\0') &&
(val != std::numeric_limits<double>::infinity());
if (isConversionOkay) {
out_double = val;
}
......@@ -57,6 +62,18 @@ static double InstructionParameterToDouble(const xacc::InstructionParameter& in_
class CompositeInstruction;
struct CompositeArgument {
std::string name;
std::string type;
HeterogeneousMap runtimeValue;
CompositeArgument(const std::string n, const std::string t) {
name = n;
type = t;
}
};
const std::string INTERNAL_ARGUMENT_VALUE_KEY = "__xacc_internal_value_key";
// The Instruction interface exposes an API for describing a general
// post-Moore's law low-level assembly instruction. Each Instruction
// exposes a unique string name, the vector of bit indices that it operates on,
......@@ -81,6 +98,10 @@ class Instruction : public BaseInstructionVisitable,
public Cloneable<Instruction> {
public:
virtual void applyRuntimeArguments() = 0;
virtual void addArgument(std::shared_ptr<CompositeArgument> arg,
const int idx_of_inst_param) = 0;
virtual const std::string toString() = 0;
virtual const std::vector<std::size_t> bits() = 0;
......@@ -90,8 +111,10 @@ public:
// For the case where the bit indices for this Instruction are
// some general expression, Clients should set the bit to -1 and provide
// the bit expression as a string. The example here would be
// H(q[i]) (bit_idx=0, expr=i), or C(q[i],q[i+1]) (bit_idx=0, expr=i and bit_idx=1,expr=i+1)
virtual void setBitExpression(const std::size_t bit_idx, const std::string expr) = 0;
// H(q[i]) (bit_idx=0, expr=i), or C(q[i],q[i+1]) (bit_idx=0, expr=i and
// bit_idx=1,expr=i+1)
virtual void setBitExpression(const std::size_t bit_idx,
const std::string expr) = 0;
virtual std::string getBitExpression(const std::size_t bit_idx) = 0;
// Return the name of the AcceleratorBuffer that this
......@@ -102,13 +125,17 @@ public:
// input vector should be same size as bits()
virtual std::string getBufferName(const std::size_t bitIdx) = 0;
virtual std::vector<std::string> getBufferNames() = 0;
virtual void setBufferNames(const std::vector<std::string> bufferNamesPerIdx) = 0;
virtual void
setBufferNames(const std::vector<std::string> bufferNamesPerIdx) = 0;
virtual const InstructionParameter getParameter(const std::size_t idx) const = 0;
virtual const InstructionParameter
getParameter(const std::size_t idx) const = 0;
virtual std::vector<InstructionParameter> getParameters() = 0;
virtual void setParameter(const std::size_t idx, InstructionParameter &inst) = 0;
virtual void setParameter(const std::size_t idx, InstructionParameter &&inst) {
virtual void setParameter(const std::size_t idx,
InstructionParameter &inst) = 0;
virtual void setParameter(const std::size_t idx,
InstructionParameter &&inst) {
setParameter(idx, inst);
}
......@@ -118,14 +145,16 @@ public:
virtual bool isComposite() { return false; }
// The following accomadate pulse-level instructions
virtual std::string channel() {return "default";}
virtual void setChannel(const std::string ch) {return;}
virtual std::size_t start() {return 0;}
virtual std::string channel() { return "default"; }
virtual void setChannel(const std::string ch) { return; }
virtual std::size_t start() { return 0; }
virtual void setStart(const std::size_t s) { return; }
virtual std::size_t duration() { return 0; }
virtual void setDuration(const std::size_t d) { return; }
virtual void setSamples(const std::vector<std::vector<double>> samples) {return;}
virtual std::vector<std::vector<double>> getSamples() {return {};}
virtual void setSamples(const std::vector<std::vector<double>> samples) {
return;
}
virtual std::vector<std::vector<double>> getSamples() { return {}; }
// The following accomodate conditional instructions
virtual bool isEnabled() { return true; }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment