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

@ -1,5 +1,7 @@
#include "ComplexNumber.h"
#include <cmath>
ComplexNumber::ComplexNumber(double real, double imaginary)
: mReal(real),
mImaginary(imaginary)
@ -26,3 +28,8 @@ void ComplexNumber::setImaginary(double value)
{
mImaginary = value;
}
double ComplexNumber::getMagnitude() const
{
return std::sqrt(mReal * mReal + mImaginary * mImaginary);
}

View file

@ -9,6 +9,8 @@ public:
double getImaginary() const;
double getMagnitude() const;
void setReal(double value);
void setImaginary(double value);

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

@ -16,3 +16,13 @@ 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;
};

View file

@ -1,6 +1,7 @@
list(APPEND UNIT_TEST_FILES
TestBlochSphereNode.cpp
TestQuantumCircuitParsing.cpp
)
add_executable(quantum_computing_unit_tests ${CMAKE_SOURCE_DIR}/test/test_runner.cpp ${UNIT_TEST_FILES})

View file

@ -0,0 +1,24 @@
#include "TestFramework.h"
#include "TestUtils.h"
#include "TestRenderUtils.h"
#include "QuantumCircuitReader.h"
#include "QuantumCircuitNode.h"
#include "QuantumCircuit.h"
TEST_CASE(TestQuantumCircuitParsing, "quantum_computing")
{
QuantumCircuitReader reader;
auto circuit = reader.read(TestUtils::getTestDataDir() / "quantum_circuit.dat");
TestRenderer renderer(100, 100);
auto node = std::make_unique<QuantumCircuitNode>(Point(10, 10));
node->setContent(circuit.get());
renderer.getScene()->addNode(node.get());
renderer.writeSvg(TestUtils::getTestOutputDir(__FILE__) / "circuit.svg");
}

View file

@ -21,6 +21,26 @@ LatexMathExpression::~LatexMathExpression()
}
const LatexMathSymbol& LatexMathExpression::getLeftSymbol() const
{
return mLeftSymbol;
}
const LatexMathSymbol& LatexMathExpression::getRightSymbol() const
{
return mRightSymbol;
}
void LatexMathExpression::setLeftSymbol(const LatexMathSymbol& symbol)
{
mLeftSymbol = symbol;
}
void LatexMathExpression::setRightSymbol(const LatexMathSymbol& symbol)
{
mRightSymbol = symbol;
}
void LatexMathExpression::onTagBodyChar(char c)
{
if (c == '}')
@ -175,13 +195,13 @@ void LatexMathExpression::onFinishedTagName()
if (*lookup == LatexMathSymbol::Tag::FRAC)
{
mWorkingType = Type::FRACTION;
auto expression = std::make_unique<LatexMathExpression>(mWorkingType);
mWorkingExpression = expression.get();
mExpressions.push_back(std::move(expression));
onFracTag();
mWorkingString.clear();
}
else if (auto tag_type = LatexSymbolLookup::getTagType(*lookup); tag_type == LatexMathSymbol::Type::ENCLOSING_TAG)
{
onEnclosingTag(*lookup);
}
mLineState = LineState::IN_TAG_BODY;
}
}
@ -193,12 +213,39 @@ void LatexMathExpression::onFinishedTagName()
}
}
void LatexMathExpression::onFracTag()
{
mWorkingType = Type::FRACTION;
auto expression = std::make_unique<LatexMathExpression>(mWorkingType);
mWorkingExpression = expression.get();
mExpressions.push_back(std::move(expression));
}
void LatexMathExpression::onEnclosingTag(LatexMathSymbol::Tag tag)
{
mWorkingType = Type::ENCLOSING;
auto expression = std::make_unique<LatexMathExpression>(mWorkingType);
auto left_symbol_value = LatexSymbolLookup::getLeftSymbolUtf8(tag);
auto right_symbol_value = LatexSymbolLookup::getRightSymbolUtf8(tag);
MLOG_INFO("Adding enclosing tag");
expression->setLeftSymbol({ LatexMathSymbol::Type::ENCLOSING_TAG, "left", *left_symbol_value });
expression->setRightSymbol({ LatexMathSymbol::Type::ENCLOSING_TAG, "right", *right_symbol_value });
mWorkingExpression = expression.get();
mExpressions.push_back(std::move(expression));
}
void LatexMathExpression::onCloseExpression()
{
if (mWorkingType == Type::LEAF)
{
MLOG_INFO("onCloseExpression LEAF");
if (!mWorkingSymbols.empty())
{
MLOG_INFO("Has working symbols");
auto expression = std::make_unique<LatexMathExpression>(mWorkingType);
expression->setContent(mWorkingSymbols);
mExpressions.push_back(std::move(expression));
@ -206,6 +253,7 @@ void LatexMathExpression::onCloseExpression()
}
else if (mWorkingType == Type::FRACTION)
{
MLOG_INFO("onCloseExpression FRACTION");
mWorkingExpression->setRawContent(mWorkingString);
if (mWorkingExpression->getExpressions().size() == 2)
@ -219,6 +267,20 @@ void LatexMathExpression::onCloseExpression()
mOpenBraceCount = 0;
}
}
else if (mWorkingType == Type::ENCLOSING)
{
MLOG_INFO("onCloseExpression ENCLOSING");
if (!mWorkingString.empty())
{
mWorkingExpression->setRawContent(mWorkingString);
if (mWorkingExpression->getExpressions().size() == 1)
{
mWorkingType = Type::LEAF;
mLineState = LineState::NONE;
}
}
}
mWorkingString.clear();
mWorkingSymbols.clear();

View file

@ -17,6 +17,7 @@ public:
LINEAR,
LEAF,
FRACTION,
ENCLOSING,
SUBSCRIPT,
SUPERSCRIPT
};
@ -39,8 +40,16 @@ public:
const std::vector<LatexMathExpressionPtr>& getExpressions() const;
const LatexMathSymbol& getLeftSymbol() const;
const LatexMathSymbol& getRightSymbol() const;
const Type getType() const;
void setLeftSymbol(const LatexMathSymbol& symbol);
void setRightSymbol(const LatexMathSymbol& symbol);
void setContent(std::vector<LatexMathSymbol>& symbols);
void setRawContent(const std::string& content);
@ -67,6 +76,10 @@ public:
void onCloseExpression();
void onFracTag();
void onEnclosingTag(LatexMathSymbol::Tag tag);
void parse();
private:
@ -81,6 +94,9 @@ private:
Type mType{ Type::LEAF };
std::vector<LatexMathSymbol> mWorkingSymbols;
LatexMathSymbol mLeftSymbol;
LatexMathSymbol mRightSymbol;
std::vector<LatexMathSymbol> mSymbols;
std::vector<LatexMathExpressionPtr> mExpressions;
};

View file

@ -7,14 +7,51 @@ std::unordered_map<std::string, std::wstring> LatexSymbolLookup::mSymbols = {
{"alpha", L"\u03B1"},
{"beta", L"\u03B2"},
{"gamma", L"\u03B3"},
{"psi", L"\u03C8"}
{"psi", L"\u03C8"},
{"langle", L"\u27E8"},
{"rangle", L"\u27E9"},
};
std::unordered_map<LatexMathSymbol::Tag, std::wstring> LatexSymbolLookup::mLeftSymbols = {
{LatexMathSymbol::Tag::BRA, L"\u27E8"},
{LatexMathSymbol::Tag::KET, L"\u007C"}
};
std::unordered_map<LatexMathSymbol::Tag, std::wstring> LatexSymbolLookup::mRightSymbols = {
{LatexMathSymbol::Tag::BRA, L"\u007C"},
{LatexMathSymbol::Tag::KET, L"\u27E9"}
};
std::unordered_map<std::string, LatexMathSymbol::Tag> LatexSymbolLookup::mTags = {
{"frac", LatexMathSymbol::Tag::FRAC},
{"bra", LatexMathSymbol::Tag::BRA},
{"ket", LatexMathSymbol::Tag::KET},
};
std::optional<std::string> LatexSymbolLookup::getLeftSymbolUtf8(LatexMathSymbol::Tag tag)
{
if (auto entry = getLeftSymbolUtf16(tag); entry)
{
return UnicodeUtils::utf16ToUtf8String(*entry);
}
else
{
return std::nullopt;
}
}
std::optional<std::string> LatexSymbolLookup::getRightSymbolUtf8(LatexMathSymbol::Tag tag)
{
if (auto entry = getRightSymbolUtf16(tag); entry)
{
return UnicodeUtils::utf16ToUtf8String(*entry);
}
else
{
return std::nullopt;
}
}
std::optional<std::string> LatexSymbolLookup::getSymbolUtf8(const std::string& tag)
{
if (auto entry = getSymbolUtf16(tag); entry)
@ -27,6 +64,31 @@ std::optional<std::string> LatexSymbolLookup::getSymbolUtf8(const std::string& t
}
}
std::optional<std::wstring> LatexSymbolLookup::getLeftSymbolUtf16(LatexMathSymbol::Tag tag)
{
if (auto iter = mLeftSymbols.find(tag); iter != mLeftSymbols.end())
{
return iter->second;
}
else
{
return std::nullopt;
}
}
std::optional<std::wstring> LatexSymbolLookup::getRightSymbolUtf16(LatexMathSymbol::Tag tag)
{
if (auto iter = mRightSymbols.find(tag); iter != mRightSymbols.end())
{
return iter->second;
}
else
{
return std::nullopt;
}
}
std::optional<std::wstring> LatexSymbolLookup::getSymbolUtf16(const std::string& tag)
{
if (auto iter = mSymbols.find(tag); iter != mSymbols.end())

View file

@ -19,10 +19,12 @@ struct LatexMathSymbol
enum class Tag
{
NONE,
FRAC
FRAC,
BRA,
KET
};
LatexMathSymbol(Type type, const std::string& name, const std::string& unicode)
LatexMathSymbol(Type type = Type::RAW, const std::string& name = {}, const std::string& unicode = {})
: mType(type),
mName(name),
mUnicode(unicode)
@ -38,8 +40,16 @@ struct LatexMathSymbol
class LatexSymbolLookup
{
public:
static std::optional<std::string> getLeftSymbolUtf8(LatexMathSymbol::Tag tag);
static std::optional<std::string> getRightSymbolUtf8(LatexMathSymbol::Tag tag);
static std::optional<std::string> getSymbolUtf8(const std::string& tag);
static std::optional<std::wstring> getLeftSymbolUtf16(LatexMathSymbol::Tag tag);
static std::optional<std::wstring> getRightSymbolUtf16(LatexMathSymbol::Tag tag);
static std::optional<std::wstring> getSymbolUtf16(const std::string& tag);
static std::optional<LatexMathSymbol::Tag> getKnownTag(const std::string& tag);
@ -50,12 +60,32 @@ public:
{
case LatexMathSymbol::Tag::FRAC:
return 2;
case LatexMathSymbol::Tag::BRA:
case LatexMathSymbol::Tag::KET:
return 1;
default:
return 1;
}
}
static LatexMathSymbol::Type getTagType(LatexMathSymbol::Tag tag)
{
switch (tag)
{
case LatexMathSymbol::Tag::FRAC:
return LatexMathSymbol::Type::TAG;
case LatexMathSymbol::Tag::BRA:
case LatexMathSymbol::Tag::KET:
return LatexMathSymbol::Type::ENCLOSING_TAG;
default:
return LatexMathSymbol::Type::TAG;
}
}
private:
static std::unordered_map<LatexMathSymbol::Tag, std::wstring> mLeftSymbols;
static std::unordered_map<LatexMathSymbol::Tag, std::wstring> mRightSymbols;
static std::unordered_map<std::string, std::wstring> mSymbols;
static std::unordered_map<std::string, LatexMathSymbol::Tag> mTags;

View file

@ -85,6 +85,33 @@ void EquationNode::addExpression(SceneInfo* sceneInfo, const LatexMathExpression
mTextCursor.move(0.0, -9.0);
}
}
else if (expression->getType() == LatexMathExpression::Type::ENCLOSING)
{
MLOG_INFO("Processing enclosing expr");
if (expression->getExpressions().size() == 1)
{
auto left_content = expression->getLeftSymbol().mUnicode;
auto left_node = std::make_unique<TextNode>(left_content, Transform(mTextCursor));
left_node->setFont(mDefaultFont);
addChild(left_node.get());
mTextCursor.move(left_node->getContentWidth(sceneInfo), 0.0);
mText.push_back(std::move(left_node));
addExpression(sceneInfo, expression->getExpressions()[0].get());
auto right_content = expression->getRightSymbol().mUnicode;
auto right_node = std::make_unique<TextNode>(right_content, Transform(mTextCursor));
right_node->setFont(mDefaultFont);
addChild(right_node.get());
mTextCursor.move(right_node->getContentWidth(sceneInfo), 0.0);
mText.push_back(std::move(right_node));
}
}
}
void EquationNode::createOrUpdateGeometry(SceneInfo* sceneInfo)

View file

@ -0,0 +1,2 @@
|0>---X---
|1>---Z---

View file

@ -10,7 +10,7 @@
TEST_CASE(TestLatexConverter, "publishing")
{
auto expression = std::make_unique<LatexMathExpression>("\\psi = \\frac{\\alpha + \\beta}{c + d} + e");
auto expression = std::make_unique<LatexMathExpression>("\\psi = \\frac{\\alpha + \\beta}{c + d} + \\ket{e}");
auto equation_node = std::make_unique<EquationNode>(Point(10, 10));
equation_node->setContent(expression.get());