Initial quantum circuit.

This commit is contained in:
jmsgrogan 2023-01-26 11:27:35 +00:00
parent 77ce58c612
commit 20c13c1cdf
38 changed files with 1153 additions and 14 deletions

View file

@ -0,0 +1,21 @@
#pragma once
#include "QuantumGate.h"
class XQuantumGate : public OneInOneOutQuantumGate
{
public:
GateType getGateType() const override
{
return GateType::X;
}
};
class ZQuantumGate : public OneInOneOutQuantumGate
{
public:
GateType getGateType() const override
{
return GateType::Z;
}
};

View file

@ -1,31 +1,50 @@
set(PLUGIN_NAME quantum_computing)
list(APPEND quantum_computing_HEADERS
list(APPEND HEADERS
QuantumCircuit.h
BlochSphere.h
QuantumState.h
Qubit.h
QuantumGate.h
QuantumOperator.h
QuantumCircuitReader.h
QuantumCircuitElement.h
QuantumWire.h
QuantumTerminal.h
BasicQuantumGates.h
visuals/BlochSphereNode.h
visuals/QuantumCircuitNode.h
visuals/QuantumGateNode.h
visuals/QuantumWireNode.h
visuals/QuantumTerminalNode.h
)
list(APPEND quantum_computing_LIB_INCLUDES
list(APPEND SOURCES
QuantumCircuit.cpp
BlochSphere.cpp
QuantumState.cpp
Qubit.cpp
QuantumGate.cpp
QuantumOperator.cpp
QuantumCircuitReader.cpp
QuantumCircuitElement.cpp
QuantumWire.cpp
QuantumTerminal.cpp
BasicQuantumGates.cpp
visuals/BlochSphereNode.cpp
visuals/QuantumCircuitNode.cpp
visuals/QuantumGateNode.cpp
visuals/QuantumWireNode.cpp
visuals/QuantumTerminalNode.cpp
visuals/BlochSphereNode.cpp
)
add_library(${PLUGIN_NAME} SHARED ${quantum_computing_LIB_INCLUDES} ${quantum_computing_HEADERS})
add_library(${PLUGIN_NAME} SHARED ${SOURCES} ${HEADERS})
target_include_directories(${PLUGIN_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/visuals
)
target_link_libraries(${PLUGIN_NAME} PUBLIC core visual_elements ntk_math)
target_link_libraries(${PLUGIN_NAME} PUBLIC core visual_elements ntk_math publishing)
set_property(TARGET ${PLUGIN_NAME} PROPERTY FOLDER plugins)
set_target_properties( ${PLUGIN_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )

View file

@ -0,0 +1,47 @@
#include "QuantumCircuit.h"
#include "QuantumGate.h"
void QuantumCircuit::addInputTerminal(QuantumTerminalPtr terminal)
{
mInputTerminals.push_back(terminal.get());
mElements.push_back(std::move(terminal));
}
void QuantumCircuit::addOutputTerminal(QuantumTerminalPtr terminal)
{
mOutputTerminals.push_back(terminal.get());
mElements.push_back(std::move(terminal));
}
void QuantumCircuit::addQuantumWire(QuantumWirePtr wire)
{
mWires.push_back(wire.get());
mElements.push_back(std::move(wire));
}
void QuantumCircuit::addLogicGate(QuantumGatePtr gate)
{
mGates.push_back(gate.get());
mElements.push_back(std::move(gate));
}
const std::vector<QuantumTerminal*>& QuantumCircuit::getInputTerminals() const
{
return mInputTerminals;
}
const std::vector<QuantumTerminal*>& QuantumCircuit::getOutputTerminals() const
{
return mOutputTerminals;
}
const std::vector<QuantumGate*>& QuantumCircuit::getLogicGates() const
{
return mGates;
}
const std::vector<QuantumWire*>& QuantumCircuit::getQuantumWires() const
{
return mWires;
}

View file

@ -0,0 +1,37 @@
#pragma once
#include "QuantumCircuitElement.h"
#include "QuantumGate.h"
#include "QuantumTerminal.h"
#include "QuantumWire.h"
#include <vector>
class QuantumCircuit
{
public:
void addInputTerminal(QuantumTerminalPtr terminal);
void addOutputTerminal(QuantumTerminalPtr terminal);
void addQuantumWire(QuantumWirePtr wire);
void addLogicGate(QuantumGatePtr gate);
const std::vector<QuantumTerminal*>& getInputTerminals() const;
const std::vector<QuantumTerminal*>& getOutputTerminals() const;
const std::vector<QuantumGate*>& getLogicGates() const;
const std::vector<QuantumWire*>& getQuantumWires() const;
private:
std::vector<QuantumTerminal*> mInputTerminals;
std::vector<QuantumTerminal*> mOutputTerminals;
std::vector<QuantumWire*> mWires;
std::vector<QuantumGate*> mGates;
std::vector<std::unique_ptr<QuantumCircuitElement> > mElements;
};

View file

@ -0,0 +1,20 @@
#pragma once
#include <memory>
class QuantumCircuitElement
{
public:
enum class Type
{
INPUT_TERMINAL,
OUTPUT_TERMINAL,
WIRE,
GATE,
UNKNOWN
};
virtual ~QuantumCircuitElement() = default;
virtual QuantumCircuitElement::Type getType() const = 0;
};

View file

@ -0,0 +1,204 @@
#include "QuantumCircuitReader.h"
#include "QuantumCircuit.h"
#include "Qubit.h"
#include "QuantumTerminal.h"
#include "BasicQuantumGates.h"
#include "QuantumWire.h"
#include "File.h"
#include "StringUtils.h"
#include "FileLogger.h"
std::unique_ptr<QuantumCircuit> QuantumCircuitReader::read(const Path& path)
{
File file(path);
return read(file.readText());
}
std::unique_ptr<QuantumCircuit> QuantumCircuitReader::read(const std::string& content)
{
auto circuit = std::make_unique<QuantumCircuit>();
mWorkingCircuit = circuit.get();
std::size_t cursor = 0;
for (const auto& line : StringUtils::toLines(content))
{
onLine(line, cursor);
cursor++;
}
return circuit;
}
void QuantumCircuitReader::onLine(const std::string& line, std::size_t jdx)
{
mWorkingString.clear();
std::size_t cursor = 0;
while (cursor < line.size())
{
const auto c = line[cursor];
MLOG_INFO("Working char: " << std::string(1, c));
if (c == '|')
{
if (cursor + 1 < line.size())
{
if (line[cursor + 1] == '-')
{
onVertialQuantumWire({ cursor, jdx });
}
else if (auto ket = checkForKet(line.substr(cursor + 1, line.size() - cursor)); !ket.empty())
{
onKet({ cursor, jdx }, ket);
cursor += ket.size() + 1;
}
}
else
{
mWorkingString += c;
}
}
else if (c == '-')
{
if (cursor + 1 < line.size())
{
const auto end_id = getWireEnd(line.substr(cursor, line.size() - cursor));
MLOG_INFO("Wire: " << cursor << " with length " << end_id);
cursor += end_id - 1;
}
else
{
mWorkingString += c;
}
}
else
{
if (cursor + 1 < line.size())
{
auto gate = checkForGate(line.substr(cursor, line.size() - cursor));
onGate({ cursor, jdx }, gate);
cursor += gate.size() - 1;
}
else
{
mWorkingString += c;
}
}
cursor++;
}
onLineEnd();
}
void QuantumCircuitReader::onLineEnd()
{
auto output_terminal = std::make_unique<QuantumTerminal>(QuantumTerminal::TerminalType::OUTPUT);
auto wire = std::make_unique<QuantumWire>(mWorkingElement, output_terminal.get());
mWorkingCircuit->addQuantumWire(std::move(wire));
mWorkingCircuit->addOutputTerminal(std::move(output_terminal));
mWorkingElement = nullptr;
}
void QuantumCircuitReader::onGate(Location loc, const std::string value)
{
MLOG_INFO("Got gate: " << value);
QuantumGatePtr gate;
if (value == "X")
{
gate = std::make_unique<XQuantumGate>();
}
else if (value == "Z")
{
gate = std::make_unique<ZQuantumGate>();
}
if (gate)
{
auto wire = std::make_unique<QuantumWire>(mWorkingElement, gate.get());
mWorkingCircuit->addQuantumWire(std::move(wire));
mWorkingElement = gate.get();
mWorkingCircuit->addLogicGate(std::move(gate));
}
}
std::string QuantumCircuitReader::checkForGate(const std::string& segment)
{
std::string working_string;
for (const auto c : segment)
{
if (c == '-')
{
break;
}
working_string += c;
}
return working_string;
}
void QuantumCircuitReader::onKet(Location loc, const std::string value)
{
MLOG_INFO("Got input state: " << value);
Qubit qubit;
if (value == "1")
{
qubit = Qubit({ 0.0, 0.0 }, { 1.0, 0.0 });
}
auto input_terminal = std::make_unique<QuantumTerminal>(QuantumTerminal::TerminalType::INPUT);
mWorkingElement = input_terminal.get();
mWorkingCircuit->addInputTerminal(std::move(input_terminal));
}
std::size_t QuantumCircuitReader::getWireEnd(const std::string& segment)
{
std::size_t idx = 0;
for (const auto c : segment)
{
if (c != '-')
{
break;
}
idx++;
}
return idx;
}
std::string QuantumCircuitReader::checkForKet(const std::string& segment)
{
std::string working_string;
bool found{ false };
for (const auto c : segment)
{
if (c == '>')
{
found = true;
break;
}
else
{
working_string += c;
}
}
if (found)
{
return working_string;
}
else
{
return {};
}
}
void QuantumCircuitReader::onVertialQuantumWire(Location loc)
{
}

View file

@ -0,0 +1,40 @@
#pragma once
#include <filesystem>
#include <string>
using Path = std::filesystem::path;
class QuantumCircuit;
class QuantumCircuitElement;
class QuantumCircuitReader
{
public:
std::unique_ptr<QuantumCircuit> read(const Path& path);
std::unique_ptr<QuantumCircuit> read(const std::string& content);
private:
using Location = std::pair<std::size_t, std::size_t>;
void onLine(const std::string& line, std::size_t jdx);
std::string checkForKet(const std::string& segment);
std::string checkForGate(const std::string& segment);
std::size_t getWireEnd(const std::string& segment);
void onGate(Location loc, const std::string value);
void onKet(Location loc, const std::string value);
void onLineEnd();
void onVertialQuantumWire(Location loc);
std::string mWorkingString;
QuantumCircuitElement* mWorkingElement{ nullptr };
QuantumCircuit* mWorkingCircuit{ nullptr };
};

View file

@ -0,0 +1,112 @@
#include "QuantumGate.h"
NInMOutQuantumGate::NInMOutQuantumGate(std::size_t numIn, std::size_t numOut, std::vector<AbstractQuantumWire*> inputs, std::vector<AbstractQuantumWire*> outputs)
: QuantumGate(),
mNumIn(numIn),
mNumOut(numOut)
{
if (inputs.size() == mNumIn)
{
mInputs = inputs;
}
else
{
mInputs = std::vector<AbstractQuantumWire*>(numIn, nullptr);
}
if (outputs.size() == mNumOut)
{
mOutputs = outputs;
}
else
{
mOutputs = std::vector<AbstractQuantumWire*>(numOut, nullptr);
}
}
std::size_t NInMOutQuantumGate::getNumInputs() const
{
return mNumIn;
}
std::size_t NInMOutQuantumGate::getNumOutputs() const
{
return mNumOut;
}
AbstractQuantumWire* NInMOutQuantumGate::getInput(std::size_t idx) const
{
if (idx < mNumIn)
{
return mInputs[idx];
}
else
{
return nullptr;
}
}
AbstractQuantumWire* NInMOutQuantumGate::getOutput(std::size_t idx) const
{
if (idx < mNumOut)
{
return mOutputs[idx];
}
else
{
return nullptr;
}
}
void NInMOutQuantumGate::setAtInput(std::size_t idx, AbstractQuantumWire* value)
{
if (idx < mInputs.size())
{
mInputs[idx] = value;
}
}
void NInMOutQuantumGate::setAtOutput(std::size_t idx, AbstractQuantumWire* value)
{
if (idx < mOutputs.size())
{
mOutputs[idx] = value;
}
}
TwoInOneOutQuantumGate::TwoInOneOutQuantumGate(AbstractQuantumWire* input0, AbstractQuantumWire* input1, AbstractQuantumWire* output)
: NInMOutQuantumGate(2, 1, { input0, input1 }, { output })
{
}
void TwoInOneOutQuantumGate::setInput0(AbstractQuantumWire* input)
{
setAtInput(0, input);
}
void TwoInOneOutQuantumGate::setInput1(AbstractQuantumWire* input)
{
setAtInput(1, input);
}
void TwoInOneOutQuantumGate::setOutput(AbstractQuantumWire* output)
{
setAtOutput(0, output);
}
OneInOneOutQuantumGate::OneInOneOutQuantumGate(AbstractQuantumWire* input, AbstractQuantumWire* output)
: NInMOutQuantumGate(1, 1, { input }, { output })
{
}
void OneInOneOutQuantumGate::setInput(AbstractQuantumWire* input)
{
setAtInput(0, input);
}
void OneInOneOutQuantumGate::setOutput(AbstractQuantumWire* output)
{
setAtOutput(0, output);
}

View file

@ -0,0 +1,87 @@
#pragma once
#include "QuantumCircuitElement.h"
#include "QuantumWire.h"
#include <vector>
class QuantumGate : public QuantumCircuitElement
{
public:
enum class GateType
{
X,
Y,
Z,
H,
CNOT,
CUSTOM
};
virtual ~QuantumGate() = default;
virtual std::size_t getNumInputs() const = 0;
virtual std::size_t getNumOutputs() const = 0;
virtual AbstractQuantumWire* getInput(std::size_t idx) const = 0;
virtual AbstractQuantumWire* getOutput(std::size_t idx) const = 0;
virtual GateType getGateType() const = 0;
Type getType() const override
{
return Type::GATE;
}
};
using QuantumGatePtr = std::unique_ptr<QuantumGate>;
class NInMOutQuantumGate : public QuantumGate
{
public:
NInMOutQuantumGate(std::size_t numIn, std::size_t numOut, std::vector<AbstractQuantumWire*> inputs = {}, std::vector<AbstractQuantumWire*> outputs = {});
virtual ~NInMOutQuantumGate() = default;
std::size_t getNumInputs() const override;
std::size_t getNumOutputs() const override;
AbstractQuantumWire* getInput(std::size_t idx) const override;
AbstractQuantumWire* getOutput(std::size_t idx) const override;
void setAtInput(std::size_t idx, AbstractQuantumWire* value);
void setAtOutput(std::size_t idx, AbstractQuantumWire* value);
private:
std::size_t mNumIn{ 1 };
std::size_t mNumOut{ 1 };
std::vector<AbstractQuantumWire*> mInputs;
std::vector<AbstractQuantumWire*> mOutputs;
};
class TwoInOneOutQuantumGate : public NInMOutQuantumGate
{
public:
TwoInOneOutQuantumGate(AbstractQuantumWire* input0 = nullptr, AbstractQuantumWire* input1 = nullptr, AbstractQuantumWire* output = nullptr);
void setInput0(AbstractQuantumWire* input);
void setInput1(AbstractQuantumWire* input);
void setOutput(AbstractQuantumWire* output);
};
class OneInOneOutQuantumGate : public NInMOutQuantumGate
{
public:
OneInOneOutQuantumGate(AbstractQuantumWire* input = nullptr, AbstractQuantumWire* output = nullptr);
void setInput(AbstractQuantumWire* input);
void setOutput(AbstractQuantumWire* output);
};

View file

@ -0,0 +1,6 @@
#include "QuantumState.h"
const std::vector<Qubit>& QuantumState::getData() const
{
return mState;
}

View file

@ -0,0 +1,13 @@
#pragma once
#include "Qubit.h"
#include <vector>
class QuantumState
{
const std::vector<Qubit>& getData() const;
private:
std::vector<Qubit> mState;
};

View file

@ -0,0 +1,33 @@
#include "QuantumTerminal.h"
QuantumTerminal::QuantumTerminal(TerminalType type, const std::string& label)
: mLabel(label),
mType(type)
{
}
QuantumTerminal::Type QuantumTerminal::getType() const
{
return mType == TerminalType::INPUT ? Type::INPUT_TERMINAL : Type::OUTPUT_TERMINAL;
}
const Qubit& QuantumTerminal::getValue() const
{
return mValue;
}
QuantumWire* QuantumTerminal::getConnection() const
{
return mConnection;
}
void QuantumTerminal::setConnection(QuantumWire* connection)
{
mConnection = connection;
}
void QuantumTerminal::setValue(const Qubit& value)
{
mValue = value;
}

View file

@ -0,0 +1,38 @@
#pragma once
#include "QuantumCircuitElement.h"
#include "Qubit.h"
#include <string>
#include <memory>
class QuantumWire;
class QuantumTerminal : public QuantumCircuitElement
{
public:
enum class TerminalType
{
INPUT,
OUTPUT
};
QuantumTerminal(TerminalType type, const std::string& label = {});
QuantumWire* getConnection() const;
Type getType() const override;
const Qubit& getValue() const;
void setConnection(QuantumWire* connection);
void setValue(const Qubit& value);
private:
std::string mLabel;
TerminalType mType;
Qubit mValue;
QuantumWire* mConnection{ nullptr };
};
using QuantumTerminalPtr = std::unique_ptr<QuantumTerminal>;

View file

@ -0,0 +1,28 @@
#include "QuantumWire.h"
QuantumWire::QuantumWire(QuantumCircuitElement* input, QuantumCircuitElement* output)
: mInput(input),
mOutput(output)
{
}
QuantumCircuitElement* QuantumWire::getInput() const
{
return mInput;
}
QuantumCircuitElement* QuantumWire::getOutput() const
{
return mOutput;
}
QuantumWire::Type QuantumWire::getType() const
{
return Type::WIRE;
}
QuantumWire::WireType QuantumWire::getWireType() const
{
return WireType::QUANTUM;
}

View file

@ -0,0 +1,42 @@
#pragma once
#include "QuantumCircuitElement.h"
class AbstractQuantumWire : public QuantumCircuitElement
{
public:
enum class WireType
{
QUANTUM,
CLASSICAL
};
virtual ~AbstractQuantumWire() = default;
virtual WireType getWireType() const = 0;
};
class QuantumWire : public AbstractQuantumWire
{
public:
QuantumWire(QuantumCircuitElement* input, QuantumCircuitElement* output);
QuantumCircuitElement* getInput() const;
QuantumCircuitElement* getOutput() const;
Type getType() const override;
WireType getWireType() const override;
private:
QuantumCircuitElement* mInput{ nullptr };
QuantumCircuitElement* mOutput{ nullptr };
};
using QuantumWirePtr = std::unique_ptr<QuantumWire>;
class ClassicalWire : public AbstractQuantumWire
{
public:
WireType getWireType() const override
{
return WireType::CLASSICAL;
}
};

View file

@ -15,4 +15,14 @@ const ComplexNumber& Qubit::getAlpha() const
const ComplexNumber& Qubit::getBeta() const
{
return mBeta;
}
bool Qubit::isIn0State() const
{
return mAlpha.getReal() == 1.0 && mBeta.getMagnitude() == 0.0;
}
bool Qubit::isIn1State() const
{
return mBeta.getReal() == 1.0 && mAlpha.getMagnitude() == 0.0;
}

View file

@ -5,11 +5,15 @@
class Qubit
{
public:
Qubit(const ComplexNumber& alpha, const ComplexNumber& beta);
Qubit(const ComplexNumber& alpha = {1.0, 0.0}, const ComplexNumber& beta = { 0.0, 0.0 });
const ComplexNumber& getAlpha() const;
const ComplexNumber& getBeta() const;
bool isIn0State() const;
bool isIn1State() const;
private:
ComplexNumber mAlpha;
ComplexNumber mBeta;

View file

@ -0,0 +1,44 @@
#include "QuantumCircuitNode.h"
#include "QuantumCircuit.h"
#include "QuantumTerminalNode.h"
QuantumCircuitNode::QuantumCircuitNode(const Transform& t)
{
}
void QuantumCircuitNode::setContent(QuantumCircuit* circuit)
{
mContent = circuit;
mContentDirty = true;
}
void QuantumCircuitNode::update(SceneInfo* sceneInfo)
{
if (mContentDirty)
{
createOrUpdateGeometry(sceneInfo);
mContentDirty = false;
}
}
void QuantumCircuitNode::createOrUpdateGeometry(SceneInfo*)
{
double terminal_vertical_spacing = 100;
double terminal_left_margin = 10;
double terminal_y = 10;
for (auto terminal : mContent->getInputTerminals())
{
Point loc{ terminal_left_margin, terminal_y };
auto terminal_node = std::make_unique<QuantumTerminalNode>(Transform(loc));
terminal_node->setContent(terminal);
addChild(terminal_node.get());
mInputTerminalNodes.push_back(std::move(terminal_node));
terminal_y += terminal_vertical_spacing;
}
}

View file

@ -0,0 +1,23 @@
#pragma once
#include "AbstractVisualNode.h"
class QuantumCircuit;
class QuantumTerminalNode;
class QuantumCircuitNode : public AbstractVisualNode
{
public:
QuantumCircuitNode(const Transform& t = {});
void setContent(QuantumCircuit* circuit);
void update(SceneInfo* sceneInfo);
private:
void createOrUpdateGeometry(SceneInfo* sceneInfo);
bool mContentDirty{ true };
QuantumCircuit* mContent{ nullptr };
std::vector<std::unique_ptr<QuantumTerminalNode> > mInputTerminalNodes;
};

View file

@ -0,0 +1,53 @@
#include "QuantumTerminalNode.h"
#include "QuantumTerminal.h"
#include "EquationNode.h"
#include "LatexMathExpression.h"
QuantumTerminalNode::QuantumTerminalNode(const Transform& transform)
: AbstractVisualNode(transform)
{
}
void QuantumTerminalNode::setContent(QuantumTerminal* terminal)
{
mContent = terminal;
mContentDirty = true;
}
void QuantumTerminalNode::update(SceneInfo* sceneInfo)
{
if (mContentDirty)
{
createOrUpdateGeometry(sceneInfo);
mContentDirty = false;
}
}
void QuantumTerminalNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
{
if (!mLabel)
{
const auto value = mContent->getValue();
std::string label;
if (value.isIn0State())
{
label = "\\ket{0}";
}
else if(value.isIn1State())
{
label = "\\ket{1}";
}
else
{
label = "\\Psi";
}
mLabelExpression = std::make_unique<LatexMathExpression>(label);
mLabel = std::make_unique<EquationNode>();
mLabel->setContent(mLabelExpression.get());
addChild(mLabel.get());
}
}

View file

@ -0,0 +1,25 @@
#pragma once
#include "AbstractVisualNode.h"
class QuantumTerminal;
class EquationNode;
class LatexMathExpression;
class QuantumTerminalNode : public AbstractVisualNode
{
public:
QuantumTerminalNode(const Transform& transform);
void setContent(QuantumTerminal* terminal);
void update(SceneInfo* sceneInfo);
private:
void createOrUpdateGeometry(SceneInfo* sceneInfo);
QuantumTerminal* mContent{ nullptr };
bool mContentDirty{ true };
std::unique_ptr<LatexMathExpression> mLabelExpression;
std::unique_ptr<EquationNode> mLabel;
};