Repository: QuTech-Delft/OpenQL (language: python)

Issue:
Tested installation on mac osx and made a few changes
- Updated setup.py to work on mac
- Updated readme to reflect this
- ran tests scripts
- ran AutoPep8 (python style guide) on setup.py and test examples

@imranashraf , could you review this and if it is OK approve this PR? 


Discussion / hints:

Fix patch:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5d2a6e8f7..d4a676438 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -202,6 +202,7 @@ add_library(ql
     "${CMAKE_CURRENT_SOURCE_DIR}/src/visualizer_common.cc"
     "${CMAKE_CURRENT_SOURCE_DIR}/src/visualizer_circuit.cc"
     "${CMAKE_CURRENT_SOURCE_DIR}/src/visualizer_interaction.cc"
+    "${CMAKE_CURRENT_SOURCE_DIR}/src/visualizer_mapping.cc"
     "${CMAKE_CURRENT_SOURCE_DIR}/src/report.cc"
     "${CMAKE_CURRENT_SOURCE_DIR}/src/utils/exception.cc"
     "${CMAKE_CURRENT_SOURCE_DIR}/src/utils/logger.cc"
diff --git a/docs/visualizer.rst b/docs/visualizer.rst
index d1fce6af3..483a861cf 100644
--- a/docs/visualizer.rst
+++ b/docs/visualizer.rst
@@ -3,7 +3,15 @@
 Visualizer
 ==========
 
-The visualizer is a special compiler pass that will display a visualization of the program being compiled by the compiler.
+The visualizer is a special compiler pass that will visualize the quantum circuit being compiled. Three different types of visualization can be generated:
+
+* ``basic circuit visualization``: displays the circuit as an abstract set of gates acting on qubits
+* ``pulse visualization``: specifically designed for the quantum hardware designed by the DiCarlo lab, this visualization displays the RF pulses that
+control the quantum hardware
+* ``a mapping graph``: shows the evolution of the mapping between logical and real qubits as the circuit runs each cycle
+* ``the qubit interaction graph``: displays the interactions between qubits in the circuit
+
+display a visualization of the program being compiled by the compiler.
 The specific look of that visualization can be controlled by the general visualization parameters, which include options for different
 kinds of labels for example, and the gate specific visualization parameters, like the color of the gate, or what kind of shape to show.
 
diff --git a/src/gate.h b/src/gate.h
index ec149cb5a..3ad3a3cce 100644
--- a/src/gate.h
+++ b/src/gate.h
@@ -181,6 +181,23 @@ typedef enum e_cond_type {
 
 const utils::UInt MAX_CYCLE = utils::MAX;
 
+struct swap_parameters {
+    utils::Bool part_of_swap = false;
+    // at the end of the swap r0 stores v0 and r1 stores v1
+    utils::Int r0 = -1;
+    utils::Int r1 = -1;
+    utils::Int v0 = -1;
+    utils::Int v1 = -1;
+
+    // default constructor
+    swap_parameters() {}
+
+    // initializer list
+    swap_parameters(utils::Bool _part_of_swap, utils::Int _r0, utils::Int _r1, utils::Int _v0, utils::Int _v1)
+        : part_of_swap(_part_of_swap), r0(_r0), r1(_r1), v0(_v0), v1(_v1)
+    {}
+};
+
 /**
  * gate interface
  */
@@ -192,7 +209,8 @@ class gate {
     utils::Vec<utils::UInt> breg_operands;        // bit operands e.g. assigned to by measure; cond_operands are separate
     utils::Vec<utils::UInt> cond_operands;        // 0, 1 or 2 bit operands of condition
     cond_type_t condition = cond_always;          // defines condition and by that number of bit operands of condition
-    utils::Int int_operand = 0;						// FIXME: move to class 'classical'
+    swap_parameters swap_params;                  // if the gate is part of a swap/move, this will contain the real and virtual qubits involved
+    utils::Int int_operand = 0;                   // FIXME: move to class 'classical'
     utils::UInt duration = 0;
     utils::Real angle = 0.0;                      // for arbitrary rotations
     utils::UInt cycle = MAX_CYCLE;                // cycle after scheduling; MAX_CYCLE indicates undefined
diff --git a/src/mapper.cc b/src/mapper.cc
index 3f2496be3..38a81b4d2 100644
--- a/src/mapper.cc
+++ b/src/mapper.cc
@@ -1127,6 +1127,10 @@ void Past::AddSwap(UInt r0, UInt r1) {
         return;
     }
 
+    // store the virtual qubits corresponding to each real qubit
+    UInt v0 = v2r.GetVirt(r0);
+    UInt v1 = v2r.GetVirt(r1);
+
     circuit circ;   // current kernel copy, clear circuit
     Str mapusemovesopt = options::get("mapusemoves");
     if (mapusemovesopt != "no" && (v2r.GetRs(r0) != rs_hasstate || v2r.GetRs(r1) != rs_hasstate)) {
@@ -1187,8 +1191,14 @@ void Past::AddSwap(UInt r0, UInt r1) {
         }
     }
     nswapsadded++;                       // for reporting at the end
+
+    // add each gate in the resulting circuit
     for (auto &gp : circ) {
         Add(gp);
+        // each gate in circ is part of a swap or move, so add the parameters
+        //TODO: uint to int conversion
+        const swap_parameters swap_params {true, (Int) r0, (Int) r1, (Int) v1, (Int) v0};
+        gp->swap_params = swap_params;
     }
 
     v2r.Swap(r0,r1);        // reflect in v2r that r0 and r1 interchanged state, i.e. update the map to reflect the swap
@@ -1301,6 +1311,13 @@ void Past::MakeReal(gate *gp, circuit &circ) {
         }
     }
     QL_DOUT("... MakeReal: new gate created for: " << real_gname << " or " << gname);
+
+    if (gp->swap_params.part_of_swap) {
+        QL_DOUT("original gate was swap/move, adding swap/move parameters for gates in decomposed circuit");
+        for (gate *gate : circ) {
+            gate->swap_params = gp->swap_params;
+        }
+    }
 }
 
 // as mapper after-burner
@@ -1339,6 +1356,13 @@ void Past::MakePrimitive(gate *gp, circuit &circ) const {
         }
     }
     QL_DOUT("... MakePrimtive: new gate created for: " << prim_gname << " or " << gname);
+
+    if (gp->swap_params.part_of_swap) {
+        QL_DOUT("original gate was swap/move, adding swap/move parameters for gates in decomposed circuit");
+        for (gate *gate : circ) {
+            gate->swap_params = gp->swap_params;
+        }
+    }
 }
 
 UInt Past::MaxFreeCycle() const {
diff --git a/src/passes/passes.cc b/src/passes/passes.cc
index eb1da1620..93e2bdf23 100644
--- a/src/passes/passes.cc
+++ b/src/passes/passes.cc
@@ -347,7 +347,8 @@ VisualizerPass::VisualizerPass(const Str &name) : AbstractPass(name) {
 void VisualizerPass::runOnProgram(quantum_program *program) {
     QL_DOUT("run VisualizerPass with name = " << getPassName() << " on program " << program->name);
     
-    ql::visualize(program, getPassOptions()["visualizer_type"].as_str(), {
+    ql::visualize(program, {
+        getPassOptions()["visualizer_type"].as_str(),
         getPassOptions()["visualizer_config_path"].as_str(),
         getPassOptions()["visualizer_waveform_mapping_path"].as_str()
     });
diff --git a/src/visualizer.h b/src/visualizer.h
index 63a4a6c70..916d47b71 100644
--- a/src/visualizer.h
+++ b/src/visualizer.h
@@ -12,11 +12,12 @@
 namespace ql {
 
 struct VisualizerConfiguration {
+    const utils::Str &visualizationType;
     const utils::Str &visualizerConfigPath;
     const utils::Str &waveformMappingPath;
 };
 
-void visualize(const quantum_program* program, const utils::Str &visualizationType, const VisualizerConfiguration &configuration);
+void visualize(const quantum_program* program, const VisualizerConfiguration &configuration);
 
 #ifdef WITH_VISUALIZER
 
diff --git a/src/visualizer_cimg.cc b/src/visualizer_cimg.cc
index 26b38f196..01c293162 100644
--- a/src/visualizer_cimg.cc
+++ b/src/visualizer_cimg.cc
@@ -18,8 +18,9 @@ Image::Image(const Int imageWidth, const Int imageHeight) : cimg((int) imageWidt
     // empty
 }
 
-void Image::fill(const Int rgb) {
-    cimg.fill((int) rgb);
+void Image::fill(const Color color) {
+    cimg.fill(255);
+    drawFilledRectangle(0, 0, cimg.width(), cimg.height(), color, 1.0f);
 }
 
 void Image::drawLine(const Int x0, const Int y0, const Int x1, const Int y1, const Color color, const Real alpha, const LinePattern pattern) {
diff --git a/src/visualizer_cimg.h b/src/visualizer_cimg.h
index 20aaefcd3..6003460b4 100644
--- a/src/visualizer_cimg.h
+++ b/src/visualizer_cimg.h
@@ -36,7 +36,7 @@ class Image {
 public:
     Image(const utils::Int imageWidth, const utils::Int imageHeight);
 
-    void fill(const utils::Int rgb);
+    void fill(const Color color);
 
     void drawLine(const utils::Int x0, const utils::Int y0, const utils::Int x1, const utils::Int y1,
                   const Color color = black, const utils::Real alpha = 1, const LinePattern pattern = LinePattern::UNBROKEN);
diff --git a/src/visualizer_circuit.cc b/src/visualizer_circuit.cc
index 1d588669b..10e3e42a3 100644
--- a/src/visualizer_circuit.cc
+++ b/src/visualizer_circuit.cc
@@ -37,38 +37,32 @@ CircuitData::CircuitData(Vec<GateProperties> &gates, const CircuitLayout &layout
     if (layout.cycles.cutting.isEnabled())  cutEmptyCycles(layout);
 }
 
-Int CircuitData::calculateAmountOfCycles(const Vec<GateProperties> &gates, const Int cycleDuration) const {
-    QL_DOUT("Calculating amount of cycles...");
-
-    // Find the highest cycle in the gate vector.
-    Int amountOfCycles = 0;
-    for (const GateProperties &gate : gates) {
-        if (gate.cycle == MAX_CYCLE) {
-            QL_IOUT("Found gate with undefined cycle index. All cycle data will be discarded and circuit will be visualized sequentially.");
-            return MAX_CYCLE;
-        }
-
-        // if (gate.cycle < 0 || gate.cycle > VISUALIZER_CYCLE_WARNING_THRESHOLD) {
-        //     FATAL("Found gate with cycle index: " << gate.cycle << ". Only indices between 0 and " 
-        //        << MAX_ALLOWED_VISUALIZER_CYCLE << " are allowed!"
-        //        << "\nMake sure gates are scheduled before calling the visualizer pass!");
-        // }
-
-        if (gate.cycle > amountOfCycles)
-            amountOfCycles = gate.cycle;
-    }
-
-    // The last gate requires a different approach, because it might have a
-    // duration of multiple cycles. None of those cycles will show up as cycle
-    // index on any other gate, so we need to calculate them seperately.
-    const Int lastGateDuration = gates.at(gates.size() - 1).duration;
-    const Int lastGateDurationInCycles = lastGateDuration / cycleDuration;
-    if (lastGateDurationInCycles > 1)
-        amountOfCycles += lastGateDurationInCycles - 1;
-
-    // Cycles start at zero, so we add 1 to get the true amount of cycles.
-    return amountOfCycles + 1; 
-}
+// Int CircuitData::calculateAmountOfCycles(const Vec<GateProperties> &gates, const Int cycleDuration) const {
+//     QL_DOUT("Calculating amount of cycles...");
+
+//     // Find the highest cycle in the gate vector.
+//     Int amountOfCycles = 0;
+//     for (const GateProperties &gate : gates) {
+//         if (gate.cycle == MAX_CYCLE) {
+//             QL_IOUT("Found gate with undefined cycle index. All cycle data will be discarded and circuit will be visualized sequentially.");
+//             return MAX_CYCLE;
+//         }
+
+//         if (gate.cycle > amountOfCycles)
+//             amountOfCycles = gate.cycle;
+//     }
+
+//     // The last gate requires a different approach, because it might have a
+//     // duration of multiple cycles. None of those cycles will show up as cycle
+//     // index on any other gate, so we need to calculate them seperately.
+//     const Int lastGateDuration = gates.at(gates.size() - 1).duration;
+//     const Int lastGateDurationInCycles = lastGateDuration / cycleDuration;
+//     if (lastGateDurationInCycles > 1)
+//         amountOfCycles += lastGateDurationInCycles - 1;
+
+//     // Cycles start at zero, so we add 1 to get the true amount of cycles.
+//     return amountOfCycles + 1; 
+// }
 
 Vec<Cycle> CircuitData::generateCycles(Vec<GateProperties> &gates, const Int cycleDuration) const {
     QL_DOUT("Generating cycles...");
@@ -76,7 +70,7 @@ Vec<Cycle> CircuitData::generateCycles(Vec<GateProperties> &gates, const Int cyc
     // Calculate the amount of cycles. If there are gates with undefined cycle
     // indices, visualize the circuit sequentially.
     Vec<Cycle> cycles;
-    int amountOfCycles = calculateAmountOfCycles(gates, cycleDuration);
+    Int amountOfCycles = calculateAmountOfCycles(gates, cycleDuration);
     if (amountOfCycles == MAX_CYCLE) {
         // Add a sequential cycle to each gate.
         amountOfCycles = 0;
@@ -314,17 +308,18 @@ void CircuitData::printProperties() const {
 // =                      Structure                      = //
 // ======================================================= //
 
-Structure::Structure(const CircuitLayout &layout, const CircuitData &circuitData) :
+Structure::Structure(const CircuitLayout &layout, const CircuitData &circuitData, const Vec<Int> minCycleWidths, const Int extendedImageHeight) :
     layout(layout),
     cellDimensions({layout.grid.getCellSize(), calculateCellHeight(layout)}),
     cycleLabelsY(layout.grid.getBorderSize()),
-    bitLabelsX(layout.grid.getBorderSize())
+    bitLabelsX(layout.grid.getBorderSize()),
+    minCycleWidths(minCycleWidths)
 {
     generateCellPositions(circuitData);
     generateBitLineSegments(circuitData);
 
     imageWidth = calculateImageWidth(circuitData);
-    imageHeight = calculateImageHeight(circuitData);
+    imageHeight = calculateImageHeight(circuitData, extendedImageHeight);
 }
 
 Int Structure::calculateCellHeight(const CircuitLayout &layout) const {
@@ -350,7 +345,7 @@ Int Structure::calculateImageWidth(const CircuitData &circuitData) const {
     return layout.bitLines.labels.getColumnWidth() + imageWidthFromCells + layout.grid.getBorderSize() * 2;
 }
 
-Int Structure::calculateImageHeight(const CircuitData &circuitData) const {
+Int Structure::calculateImageHeight(const CircuitData &circuitData, const Int extendedImageHeight) const {
     QL_DOUT("Calculating image height...");
     
     const Int rowsFromQuantum = circuitData.amountOfQubits;
@@ -363,7 +358,7 @@ Int Structure::calculateImageHeight(const CircuitData &circuitData) const {
         (rowsFromQuantum + rowsFromClassical) *
         (cellDimensions.height + (layout.bitLines.edges.areEnabled() ? layout.bitLines.edges.getThickness() : 0));
 
-    return layout.cycles.labels.getRowHeight() + heightFromOperands + layout.grid.getBorderSize() * 2;
+    return layout.cycles.labels.getRowHeight() + heightFromOperands + layout.grid.getBorderSize() * 2 + extendedImageHeight;
 }
 
 void Structure::generateCellPositions(const CircuitData &circuitData) {
@@ -373,7 +368,8 @@ void Structure::generateCellPositions(const CircuitData &circuitData) {
     Int widthFromCycles = 0;
     for (Int column = 0; column < circuitData.getAmountOfCycles(); column++) {
         const Int amountOfChunks = utoi(circuitData.getCycle(column).gates.size());
-        const Int cycleWidth = (circuitData.isCycleCut(column) ? layout.cycles.cutting.getCutCycleWidth() : (cellDimensions.width * amountOfChunks));
+        const Int cycleWidth = max(minCycleWidths[column],
+            (circuitData.isCycleCut(column) ? layout.cycles.cutting.getCutCycleWidth() : (cellDimensions.width * amountOfChunks)));
 
         const Int x0 = layout.grid.getBorderSize() + layout.bitLines.labels.getColumnWidth() + widthFromCycles;
         const Int x1 = x0 + cycleWidth;
@@ -389,12 +385,20 @@ void Structure::generateCellPositions(const CircuitData &circuitData) {
         qbitCellPositions.push_back(qColumnCells);
         // Classical cell positions.
         Vec<Position4> cColumnCells;
-        for (Int row = 0; row < circuitData.amountOfClassicalBits; row++) {
+        if (circuitData.amountOfClassicalBits > 0) {
+            for (Int row = 0; row < circuitData.amountOfClassicalBits; row++) {
+                const Int y0 = layout.grid.getBorderSize() + layout.cycles.labels.getRowHeight() + 
+                    ((layout.bitLines.classical.isGrouped() ? 0 : row) + circuitData.amountOfQubits) *
+                    (cellDimensions.height + (layout.bitLines.edges.areEnabled() ? layout.bitLines.edges.getThickness() : 0));
+                const Int y1 = y0 + cellDimensions.height;
+                cColumnCells.push_back( {x0, y0, x1, y1} );
+            }
+        } else {
             const Int y0 = layout.grid.getBorderSize() + layout.cycles.labels.getRowHeight() + 
-                ((layout.bitLines.classical.isGrouped() ? 0 : row) + circuitData.amountOfQubits) *
+                circuitData.amountOfQubits *
                 (cellDimensions.height + (layout.bitLines.edges.areEnabled() ? layout.bitLines.edges.getThickness() : 0));
-            const Int y1 = y0 + cellDimensions.height;
-            cColumnCells.push_back({x0, y0, x1, y1});
+            const Int y1 = y0;
+            cColumnCells.push_back( {x0, y0, x1, y1} );
         }
         cbitCellPositions.push_back(cColumnCells);
 
@@ -467,7 +471,7 @@ Int Structure::getCircuitTopY() const {
 }
 
 Int Structure::getCircuitBotY() const {
-    const Vec<Position4> firstColumnPositions = layout.pulses.areEnabled() ? qbitCellPositions[0] : cbitCellPositions[0];
+    const Vec<Position4> firstColumnPositions = (layout.pulses.areEnabled() || !layout.bitLines.classical.isEnabled()) ? qbitCellPositions[0] : cbitCellPositions[0];
     Position4 botPosition = firstColumnPositions[firstColumnPositions.size() - 1];
     return botPosition.y1;
 }
@@ -542,6 +546,25 @@ void Structure::printProperties() const {
 
 void visualizeCircuit(const ql::quantum_program* program, const VisualizerConfiguration &configuration)
 {
+    const Vec<GateProperties> gates = parseGates(program);
+    const Int cycleDuration = utoi(program->platform.cycle_time);
+    const Int amountOfCycles = calculateAmountOfCycles(gates, cycleDuration);
+    const Vec<Int> minCycleWidths(amountOfCycles, 0);
+
+    // Generate the image.
+    ImageOutput imageOutput = generateImage(program, configuration, minCycleWidths, 0);
+
+    // Save the image if enabled.
+    if (imageOutput.circuitLayout.saveImage) {
+        imageOutput.image.save(generateFilePath("circuit_visualization", "bmp"));
+    }
+
+    // Display the image.
+    QL_DOUT("Displaying image...");
+    imageOutput.image.display("Quantum Circuit");
+}
+
+ImageOutput generateImage(const ql::quantum_program* program, const VisualizerConfiguration &configuration, const Vec<Int> minCycleWidths, const utils::Int extendedImageHeight) {
     // Get the gate list from the program.
     QL_DOUT("Getting gate list...");
     Vec<GateProperties> gates = parseGates(program);
@@ -551,7 +574,7 @@ void visualizeCircuit(const ql::quantum_program* program, const VisualizerConfig
 
     // Parse and validate the layout and instruction configuration file.
     CircuitLayout layout = parseCircuitConfiguration(gates, configuration.visualizerConfigPath, program->platform.instruction_settings);
-    validateCircuitLayout(layout);
+    validateCircuitLayout(layout, configuration.visualizationType);
 
     // Calculate circuit properties.
     QL_DOUT("Calculating circuit properties...");
@@ -563,16 +586,16 @@ void visualizeCircuit(const ql::quantum_program* program, const VisualizerConfig
     // Initialize the circuit properties.
     CircuitData circuitData(gates, layout, cycleDuration);
     circuitData.printProperties();
-    
+
     // Initialize the structure of the visualization.
     QL_DOUT("Initializing visualization structure...");
-    Structure structure(layout, circuitData);
+    Structure structure(layout, circuitData, minCycleWidths, extendedImageHeight);
     structure.printProperties();
-    
+
     // Initialize image.
     QL_DOUT("Initializing image...");
     Image image(structure.getImageWidth(), structure.getImageHeight());
-    image.fill(255);
+    image.fill(layout.backgroundColor);
 
     // Draw the cycle labels if the option has been set.
     if (layout.cycles.labels.areEnabled()) {
@@ -588,7 +611,7 @@ void visualizeCircuit(const ql::quantum_program* program, const VisualizerConfig
     if (layout.bitLines.edges.areEnabled()) {
         drawBitLineEdges(image, layout, circuitData, structure);
     }
-
+    
     // Draw the bit line labels if enabled.
     if (layout.bitLines.labels.areEnabled()) {
         drawBitLineLabels(image, layout, circuitData, structure);
@@ -698,14 +721,7 @@ void visualizeCircuit(const ql::quantum_program* program, const VisualizerConfig
         }
     }
 
-    // Save the image if enabled.
-    if (layout.saveImage) {
-        image.save(generateFilePath("circuit_visualization", "bmp"));
-    }
-
-    // Display the image.
-    QL_DOUT("Displaying image...");
-    image.display("Quantum Circuit");
+    return {image, layout, circuitData, structure};
 }
 
 CircuitLayout parseCircuitConfiguration(Vec<GateProperties> &gates,
@@ -718,6 +734,25 @@ CircuitLayout parseCircuitConfiguration(Vec<GateProperties> &gates,
     static const std::regex trim_pattern("^(\\s)+|(\\s)+$");
     static const std::regex multiple_space_pattern("(\\s)+");
 
+    // Load the visualizer configuration file.
+    Json visualizerConfig;
+    try {
+        visualizerConfig = load_json(visualizerConfigPath);
+    } catch (Json::exception &e) {
+        QL_FATAL("Failed to load the visualization config file: \n\t" << Str(e.what()));
+    }
+
+    // Load the circuit visualization parameters.
+    Json circuitConfig;
+    if (visualizerConfig.count("circuit") == 1) {
+        circuitConfig = visualizerConfig["circuit"];
+    } else {
+        QL_WOUT("Could not find circuit configuration in visualizer configuration file. Is it named correctly?");
+    }
+
+    // Fill the layout object with the values from the config file. Any missing values will assume the default values hardcoded in the layout object.
+    CircuitLayout layout;
+
     struct VisualParameters {
         Str visual_type;
         Vec<Int> codewords;
@@ -753,7 +788,13 @@ CircuitLayout parseCircuitConfiguration(Vec<GateProperties> &gates,
                 codewords.push_back(instruction["cc_light_left_codeword"]);
                 QL_DOUT("codewords: " << codewords[0] << "," << codewords[1]);
             } else {
-                QL_WOUT("Did not find any codeword attributes for instruction: '" << gateName << "'!");
+                if (circuitConfig.count("pulses") == 1) {
+                    if (circuitConfig["pulses"].count("displayGatesAsPulses") == 1) {
+                        if (circuitConfig["pulses"]["displayGatesAsPulses"]) {
+                            QL_WOUT("Did not find any codeword attributes for instruction: '" << gateName << "'!");
+                        }
+                    }
+                }
             }
         }
 
@@ -775,30 +816,16 @@ CircuitLayout parseCircuitConfiguration(Vec<GateProperties> &gates,
         }
     }
 
-    // Load the visualizer configuration file.
-    Json visualizerConfig;
-    try {
-        visualizerConfig = load_json(visualizerConfigPath);
-    } catch (Json::exception &e) {
-        QL_FATAL("Failed to load the visualization config file: \n\t" << Str(e.what()));
-    }
-
-    // Load the circuit visualization parameters.
-    Json circuitConfig;
-    if (visualizerConfig.count("circuit") == 1) {
-        circuitConfig = visualizerConfig["circuit"];
-    } else {
-        QL_WOUT("Could not find circuit configuration in visualizer configuration file. Is it named correctly?");
-    }
-
-    // Fill the layout object with the values from the config file. Any missing values will assume the default values hardcoded in the layout object.
-    CircuitLayout layout;
-
     // Check if the image should be saved to disk.
     if (visualizerConfig.count("saveImage") == 1) {
         layout.saveImage = visualizerConfig["saveImage"];
     }
 
+    // Load background fill color.
+    if (visualizerConfig.count("backgroundColor") == 1) {
+        layout.backgroundColor = visualizerConfig["backgroundColor"];
+    }
+
     // -------------------------------------- //
     // -               CYCLES               - //
     // -------------------------------------- //
@@ -1019,11 +1046,17 @@ CircuitLayout parseCircuitConfiguration(Vec<GateProperties> &gates,
     return layout;
 }
 
-void validateCircuitLayout(CircuitLayout &layout) {
+void validateCircuitLayout(CircuitLayout &layout, const Str &visualizationType) {
     QL_DOUT("Validating layout...");
 
     //TODO: add more validation
-    
+
+    // Disable pulse visualization and cycle cutting for the mapping graph visualization.
+    if (visualizationType == "MAPPING_GRAPH") {
+        layout.cycles.cutting.setEnabled(false);
+        layout.pulses.setEnabled(false);
+    }
+
     if (layout.cycles.cutting.getEmptyCycleThreshold() < 1) {
         QL_WOUT("Adjusting 'emptyCycleThreshold' to minimum value of 1. Value in configuration file is set to "
             << layout.cycles.cutting.getEmptyCycleThreshold() << ".");
@@ -1379,6 +1412,8 @@ void drawCycleEdges(Image &image,
         const Int y0 = structure.getCircuitTopY();
         const Int y1 = structure.getCircuitBotY();
 
+        QL_DOUT("drawing edge at x = " << xCycle << ", from y0 = " << y0 << " and y1 = " << y1);
+
         image.drawLine(xCycle, y0, xCycle, y1, layout.cycles.edges.getColor(), layout.cycles.edges.getAlpha(), LinePattern::DASHED);
     }
 }
diff --git a/src/visualizer_circuit.h b/src/visualizer_circuit.h
index 60838ead5..e09cf2aa7 100644
--- a/src/visualizer_circuit.h
+++ b/src/visualizer_circuit.h
@@ -79,7 +79,6 @@ class CircuitData {
     utils::Vec<Cycle> cycles;
     utils::Vec<EndPoints> cutCycleRangeIndices;
 
-    utils::Int calculateAmountOfCycles(const utils::Vec<GateProperties> &gates, const utils::Int cycleDuration) const;
     utils::Vec<Cycle> generateCycles(utils::Vec<GateProperties> &gates, const utils::Int cycleDuration) const;
     utils::Vec<EndPoints> findCuttableEmptyRanges(const CircuitLayout &layout) const;
 
@@ -114,19 +113,21 @@ class Structure {
     utils::Int imageWidth = 0;
     utils::Int imageHeight = 0;
 
+    const utils::Vec<utils::Int> minCycleWidths;
+
     utils::Vec<utils::Vec<Position4>> qbitCellPositions;
     utils::Vec<utils::Vec<Position4>> cbitCellPositions;
     utils::Vec<utils::Pair<EndPoints, utils::Bool>> bitLineSegments;
 
     utils::Int calculateCellHeight(const CircuitLayout &layout) const;
     utils::Int calculateImageWidth(const CircuitData &circuitData) const;
-    utils::Int calculateImageHeight(const CircuitData &circuitData) const;
+    utils::Int calculateImageHeight(const CircuitData &circuitData, const utils::Int extendedImageHeight) const;
 
     void generateBitLineSegments(const CircuitData &circuitData);
     void generateCellPositions(const CircuitData &circuitData);
 
 public:
-    Structure(const CircuitLayout &layout, const CircuitData &circuitData);
+    Structure(const CircuitLayout &layout, const CircuitData &circuitData, const utils::Vec<utils::Int> minCycleWidths, const utils::Int extendedImageHeight);
 
     utils::Int getImageWidth() const;
     utils::Int getImageHeight() const;
@@ -137,6 +138,7 @@ class Structure {
     utils::Int getCircuitTopY() const;
     utils::Int getCircuitBotY() const;
 
+    utils::Int getMinCycleWidth() const;
     Dimensions getCellDimensions() const;
     Position4 getCellPosition(const utils::UInt column, const utils::UInt row, const BitType bitType) const;
     utils::Vec<utils::Pair<EndPoints, utils::Bool>> getBitLineSegments() const;
@@ -144,10 +146,18 @@ class Structure {
     void printProperties() const;
 };
 
+struct ImageOutput {
+    Image image;
+    const CircuitLayout circuitLayout;
+    const CircuitData circuitData;
+    const Structure structure;
+};
+
 void visualizeCircuit(const ql::quantum_program* program, const VisualizerConfiguration &configuration);
+ImageOutput generateImage(const ql::quantum_program* program, const VisualizerConfiguration &configuration, const utils::Vec<utils::Int> minCycleWidths, const utils::Int extendedImageHeight);
 
 CircuitLayout parseCircuitConfiguration(utils::Vec<GateProperties> &gates, const utils::Str &configPath, const utils::Json platformInstructions);
-void validateCircuitLayout(CircuitLayout &layout);
+void validateCircuitLayout(CircuitLayout &layout, const utils::Str &visualizationType);
 PulseVisualization parseWaveformMapping(const utils::Str &waveformMappingPath);
 
 utils::Vec<QubitLines> generateQubitLines(const utils::Vec<GateProperties> &gates, const PulseVisualization &pulseVisualization, const CircuitData &circuitData);
diff --git a/src/visualizer_common.cc b/src/visualizer_common.cc
index 8b49b260d..eb6679395 100644
--- a/src/visualizer_common.cc
+++ b/src/visualizer_common.cc
@@ -7,6 +7,7 @@
 #include "visualizer_common.h"
 #include "visualizer_circuit.h"
 #include "visualizer_interaction.h"
+#include "visualizer_mapping.h"
 #include "visualizer_cimg.h"
 #include "options.h"
 #include "utils/str.h"
@@ -57,17 +58,22 @@ using namespace utils;
 // [GENERAL] fix compilation error due to merge
 // [GENERAL] replace cimg calls with cimg wrapper calls
 // [GENERAL] delete default constructors in position types in visualizer_types.h
+// [GENERAL] fix isEdgeAlreadyDrawn() build error on CI
+// [MAPPING] turn pulse visualization and cycle cutting off for the mapping graph
+// [MAPPING] fix virtual qubit swapping
 
 // -- IN PROGRESS ---
-// [GENERAL] fix isEdgeAlreadyDrawn() build error on CI
 // [GENERAL] update documentation
 
 // --- FUTURE WORK ---
+// [GENERAL] fix size of image when image is too big for screen (not sure how with CImg)
+// [MAPPING] make cycle cutting work with the mapping graph
+// [MAPPING] make pulse visualization work with the mapping graph
+// [INTERACTION] apply background color option to interaction graph too
 // [GENERAL] add generating random circuits for visualization testing
-// [CIRCUIT] add background color option
 // [CIRCUIT] add connection line thickness parameter
 // [CIRCUIT] add bitline thickness parameter (maybe)
-// [CIRCUIT] allow collapsing the three qubit lines into one with an option
+// [CIRCUIT] allow collapsing the three qubit lines into one with an option for pulse visualization
 // [CIRCUIT] implement cycle cutting for pulse visualization
 // [CIRCUIT] what happens when a cycle range is cut, but one or more gates still running within that range finish earlier than the longest running gate 
 //           comprising the entire range?
@@ -80,37 +86,29 @@ using namespace utils;
 
 #ifndef WITH_VISUALIZER
 
-void visualize(const quantum_program* program, const Str &visualizationType, const VisualizerConfiguration &configuration) {
+void visualize(const quantum_program* program, const VisualizerConfiguration &configuration) {
     QL_WOUT("The visualizer is disabled. If this was not intended, and OpenQL is running on Linux or Mac, the X11 library "
          << "might be missing and the visualizer has disabled itself.");
 }
 
 #else
 
-void visualize(const quantum_program* program, const Str &visualizationType, const VisualizerConfiguration &configuration) {
+void visualize(const quantum_program* program, const VisualizerConfiguration &configuration) {
     QL_IOUT("Starting visualization...");
-    QL_IOUT("Visualization type: " << visualizationType);
-
-    // for (ql::quantum_kernel kernel : program->kernels) {
-    //     for (ql::gate* const gate : kernel.get_circuit()) {
-    //         if (gate->type() == __remap_gate__) {
-    //             const ql::remap *remap_p = dynamic_cast<ql::remap*>(gate);
-    //             IOUT("remap gate: [" << remap_p->operands[0] << ", " << remap_p->virtual_qubit_index << "]");
-    //         }
-    //     }
-    // }
+    QL_IOUT("Visualization type: " << configuration.visualizationType);
 
     // printGates(parseGates(program));
+    // if (true) return;
 
     // Choose the proper visualization based on the visualization type.
-    if (visualizationType == "CIRCUIT") {
+    if (configuration.visualizationType == "CIRCUIT") {
         visualizeCircuit(program, configuration);
-    } else if (visualizationType == "INTERACTION_GRAPH") {
+    } else if (configuration.visualizationType == "INTERACTION_GRAPH") {
         visualizeInteractionGraph(program, configuration);
-    } else if (visualizationType == "MAPPING_GRAPH") {
-        QL_WOUT("Mapping graph visualization not yet implemented.");
+    } else if (configuration.visualizationType == "MAPPING_GRAPH") {
+        visualizeMappingGraph(program, configuration);
     } else {
-        QL_FATAL("Unknown visualization type: " << visualizationType << "!");
+        QL_FATAL("Unknown visualization type: " << configuration.visualizationType << "!");
     }
 
     QL_IOUT("Visualization complete...");
@@ -129,13 +127,12 @@ Vec<GateProperties> parseGates(const quantum_program* program) {
                 gate->name,
                 operands,
                 creg_operands,
+                gate->swap_params,
                 utoi(gate->duration),
                 utoi(gate->cycle),
                 gate->type(),
                 {},
-                "UNDEFINED",
-                0
-                // gate->type() == __remap_gate__ ? dynamic_cast<ql::remap*>(gate)->virtual_qubit_index : MAX
+                "UNDEFINED"
             };
             gates.push_back(gateProperties);
         }
@@ -144,33 +141,55 @@ Vec<GateProperties> parseGates(const quantum_program* program) {
     return gates;
 }
 
+Int calculateAmountOfCycles(const Vec<GateProperties> &gates, const Int cycleDuration) {
+    QL_DOUT("Calculating amount of cycles...");
+
+    // Find the highest cycle in the gate vector.
+    Int amountOfCycles = 0;
+    for (const GateProperties &gate : gates) {
+        if (gate.cycle == MAX_CYCLE) {
+            QL_IOUT("Found gate with undefined cycle index. All cycle data will be discarded and circuit will be visualized sequentially.");
+            return MAX_CYCLE;
+        }
+
+        if (gate.cycle > amountOfCycles)
+            amountOfCycles = gate.cycle;
+    }
+
+    // The last gate requires a different approach, because it might have a
+    // duration of multiple cycles. None of those cycles will show up as cycle
+    // index on any other gate, so we need to calculate them seperately.
+    const Int lastGateDuration = gates.at(gates.size() - 1).duration;
+    const Int lastGateDurationInCycles = lastGateDuration / cycleDuration;
+    if (lastGateDurationInCycles > 1)
+        amountOfCycles += lastGateDurationInCycles - 1;
+
+    // Cycles start at zero, so we add 1 to get the true amount of cycles.
+    return amountOfCycles + 1; 
+}
+
 Int calculateAmountOfBits(const Vec<GateProperties> &gates, const Vec<Int> GateProperties::* operandType) {
     QL_DOUT("Calculating amount of bits...");
 
-    //TODO: handle circuits not starting at a c- or qbit with index 0
-    Int minAmount = MAX;
+    // Find the maximum index of the operands.
     Int maxAmount = 0;
-
-    // Find the minimum and maximum index of the operands.
-    for (const GateProperties &gate : gates)
-    {
+    for (const GateProperties &gate : gates) {
         Vec<Int>::const_iterator begin = (gate.*operandType).begin();
         const Vec<Int>::const_iterator end = (gate.*operandType).end();
 
         for (; begin != end; ++begin) {
             const Int number = *begin;
-            if (number < minAmount) minAmount = number;
             if (number > maxAmount) maxAmount = number;
         }
     }
 
-    // If both minAmount and maxAmount are at their original values, the list of 
-    // operands for all the gates was empty.This means there are no operands of 
-    // the given type for these gates and we return 0.
-    if (minAmount == MAX && maxAmount == 0) {
+    // If maxAmount is at its original value, the list of operands for all the gates
+    // was empty. This means there are no operands of the given type for these gates
+    // and we return 0.
+    if (maxAmount == 0) {
         return 0;
     } else {
-        return 1 + maxAmount - minAmount; // +1 because: max - min = #qubits - 1
+        return 1 + maxAmount;
     }
 }
 
@@ -257,10 +276,59 @@ void printGates(const Vec<GateProperties> &gates) {
         QL_IOUT("\tcodewords: " << codewords << "]");
 
         QL_IOUT("\tvisual_type: " << gate.visual_type);
+    }
+}
+
+void printGatesShort(const Vec<GateProperties> &gates) {
+    Int maxGateNameLength = 0;
+    Int maxSwapStringLength = 0;
+    Int maxCycleStringLength = 0;
+    Int maxRealOperandsLength = 0;
+    for (const GateProperties &gate : gates) {
+        if (gate.name.length() > maxGateNameLength) {
+            maxGateNameLength = gate.name.length();
+        }
+        if (to_string(gate.swap_params.part_of_swap).length() > maxSwapStringLength) {
+            maxSwapStringLength = to_string(gate.swap_params.part_of_swap).length();
+        }
+        if (to_string(gate.cycle).length() > maxCycleStringLength) {
+            maxCycleStringLength = to_string(gate.cycle).length();
+        }
+        Str rOperands = "[ "; for (const Int operand : gate.operands) {rOperands += std::to_string(operand) + " ";} rOperands += "]";
+        if (rOperands.length() > maxRealOperandsLength) {
+            maxRealOperandsLength = rOperands.length();
+        }
+    }
+    const Int minSpacing = 3;
+    for (const GateProperties &gate : gates) {
+        Str rOperands = "[ "; for (const Int operand : gate.operands) {rOperands += std::to_string(operand) + " ";} rOperands += "]";
+        Str vOperands = "[" + to_string(gate.swap_params.v0) + ", " + to_string(gate.swap_params.v1) + "]";
+
+        Str nameSectionExtraSpacing;
+        for (Int i = 0; i < maxGateNameLength - gate.name.length() + minSpacing; i++) {
+            nameSectionExtraSpacing += " ";
+        }
+        const Str nameSection = "gate: " + gate.name + nameSectionExtraSpacing;
+
+        Str swapSectionExtraSpacing;
+        for (Int i = 0; i < maxSwapStringLength - to_string(gate.swap_params.part_of_swap).length() + minSpacing; i++) {
+            swapSectionExtraSpacing += " ";
+        }
+        const Str swapSection = "part of swap: " + to_string(gate.swap_params.part_of_swap) + swapSectionExtraSpacing;
+
+        Str cycleSectionExtraSpacing;
+        for (Int i = 0; i < maxCycleStringLength - to_string(gate.cycle).length() + minSpacing; i++) {
+            cycleSectionExtraSpacing += " ";
+        }
+        const Str cycleSection = "cycle: " + to_string(gate.cycle) + cycleSectionExtraSpacing;
+
+        Str realOperandsSectionExtraSpacing;
+        for (Int i = 0; i < maxRealOperandsLength - rOperands.length() + 1; i++) {
+            realOperandsSectionExtraSpacing += " ";
+        }
+        const Str realOperandsSection = "real and virtual operands: " + rOperands + realOperandsSectionExtraSpacing;
 
-        // if (gate.type == __remap_gate__) {
-        //     QL_IOUT("\tvirtual_qubit_index: " << gate.virtual_qubit_index);
-        // }
+        QL_IOUT(nameSection << swapSection << cycleSection << realOperandsSection << " and " << vOperands);
     }
 }
 
diff --git a/src/visualizer_common.h b/src/visualizer_common.h
index 7701a7a2e..40171d2c2 100644
--- a/src/visualizer_common.h
+++ b/src/visualizer_common.h
@@ -17,6 +17,7 @@ namespace ql {
 
 utils::Vec<GateProperties> parseGates(const quantum_program* program);
 
+utils::Int calculateAmountOfCycles(const utils::Vec<GateProperties> &gates, const utils::Int cycleDuration);
 utils::Int calculateAmountOfBits(const utils::Vec<GateProperties> &gates, const utils::Vec<utils::Int> GateProperties::* operandType);
 
 utils::Int calculateAmountOfGateOperands(const GateProperties &gate);
@@ -29,6 +30,7 @@ utils::Bool isMeasurement(const GateProperties &gate);
 utils::Str generateFilePath(const utils::Str &filename, const utils::Str &extension);
 
 void printGates(const utils::Vec<GateProperties> &gates);
+void printGatesShort(const utils::Vec<GateProperties> &gates);
 
 } // namespace ql
 
diff --git a/src/visualizer_interaction.cc b/src/visualizer_interaction.cc
index 2795a612e..74e2c1c6c 100644
--- a/src/visualizer_interaction.cc
+++ b/src/visualizer_interaction.cc
@@ -62,7 +62,7 @@ void visualizeInteractionGraph(const quantum_program* program, const VisualizerC
         const Int imageWidth = 2 * (layout.getBorderWidth() + interactionCircleRadius);
         const Int imageHeight = 2 * (layout.getBorderWidth() + interactionCircleRadius);
         Image image(imageWidth, imageHeight);
-        image.fill(255);
+        image.fill(white);
 
         // Draw the edges between interacting qubits.
         Vec<Pair<Int, Int>> drawnEdges;
@@ -286,17 +286,6 @@ Bool isEdgeAlreadyDrawn(const Vec<Pair<Int, Int>> &drawnEdges, const Int first,
     return false;
 }
 
-// bool isEdgeAlreadyDrawn(const std::vector<std::pair<int, int>> &drawnEdges, const int first, const int second) {
-//     // Check if the edge already exists.
-//     for (const std::pair<int, int> &drawnEdge : drawnEdges) {
-//         if ((drawnEdge.first == first && drawnEdge.second == second) || (drawnEdge.first == second && drawnEdge.second == first)) {
-//             return true;
-//         }
-//     }
-
-//     return false;
-// }
-
 void printInteractionList(const Vec<Qubit> &qubits) {
     // Print the qubit interaction list.
     for (const Qubit &qubit : qubits) {
diff --git a/src/visualizer_mapping.cc b/src/visualizer_mapping.cc
new file mode 100644
index 000000000..7f5e6fd38
--- /dev/null
+++ b/src/visualizer_mapping.cc
@@ -0,0 +1,383 @@
+/** \file
+ * Definition of the visualizer mapping graph.
+ */
+
+#ifdef WITH_VISUALIZER
+
+#include "visualizer.h"
+#include "visualizer_types.h"
+#include "visualizer_common.h"
+#include "visualizer_cimg.h"
+#include "visualizer_circuit.h"
+#include "visualizer_mapping.h"
+#include "utils/json.h"
+#include "utils/num.h"
+#include "utils/vec.h"
+
+namespace ql {
+
+using namespace utils;
+
+void visualizeMappingGraph(const quantum_program* program, const VisualizerConfiguration &configuration) {
+    QL_IOUT("Visualizing mapping graph...");
+
+    // Parse the layout and gate vector.
+    const MappingGraphLayout layout = parseMappingGraphLayout(configuration.visualizerConfigPath);
+    Vec<GateProperties> gates = parseGates(program);
+
+    // Parse the topology if it exists in the platform configuration file.
+    Topology topology;
+    const Bool parsedTopology = layout.getUseTopology() ? parseTopology(program->platform.topology, topology) : false;
+    if (parsedTopology) {
+        QL_DOUT("Succesfully parsed topology.");
+        QL_DOUT("xSize: " << topology.xSize);
+        QL_DOUT("ySize: " << topology.ySize);
+        QL_DOUT("qubits:");
+        for (Int qubitIndex = 0; qubitIndex < topology.vertices.size(); qubitIndex++) {
+            QL_DOUT("\tid: " << qubitIndex << " position: [" << topology.vertices[qubitIndex].x << ", " << topology.vertices[qubitIndex].y << "]");
+        }
+        QL_DOUT("edges:");
+        for (const Edge edge : topology.edges) {
+            QL_DOUT("\tsrc: " << edge.src << ", dst: " << edge.dst);
+        }
+    } else {
+        QL_WOUT("Could not parse qubit topology. Falling back on basic visualization.");
+    }
+
+    // QL_IOUT("Gate input to mapping graph visualizer:");
+    // printGates(gates);
+    // printGatesShort(gates);
+
+    // Get visualized circuit with extra wide cycles from visualizer_circuit.cc.
+    const Int amountOfQubits = calculateAmountOfBits(gates, &GateProperties::operands);
+    const Int amountOfColumns = parsedTopology ? topology.xSize : ceil(sqrt(amountOfQubits));
+    const Int amountOfRows = parsedTopology ? topology.ySize : ceil(sqrt(amountOfQubits));
+    const Int qubitDiameter = layout.getQubitRadius() * 2;
+    const Int columnWidth = qubitDiameter;
+    const Int rowHeight = qubitDiameter + (layout.getShowRealIndices() ? layout.getFontHeightReal() + layout.getRealIndexSpacing() * 2 : 0);
+
+    // Calculate the virtual qubits mapping for each cycle.
+    const Int cycleDuration = utoi(program->platform.cycle_time);
+    Int amountOfCycles = calculateAmountOfCycles(gates, cycleDuration);
+    if (amountOfCycles <= 0) {
+        QL_FATAL("Circuit contains no cycles! Cannot visualize mapping graph.");
+    }
+    // Visualize the circuit sequentially if one or more gates were not scheduled yet.
+    if (amountOfCycles == MAX_CYCLE) {
+        // Add a sequential cycle to each gate.
+        amountOfCycles = 0;
+        for (GateProperties &gate : gates) {
+            gate.cycle = amountOfCycles;
+            amountOfCycles += gate.duration / cycleDuration;
+        }
+    }
+
+    // This vector stores the qubit mapping per cycle.
+    Vec<Vec<Int>> virtualQubits(amountOfCycles);
+
+    // This vector stores whether the mapping has changed for each cycle compared to the previous cycle.
+    Vec<Bool> mappingChangedPerCycle(amountOfCycles, false);
+
+    // Compute the mappings for each cycle.
+    computeMappingPerCycle(layout, virtualQubits, mappingChangedPerCycle, gates, amountOfCycles, amountOfQubits);
+    
+    // Compute the minimum cycle widths for each cycle.
+    Vec<Int> minCycleWidths(amountOfCycles, 0);
+    for (Int cycleIndex = 0; cycleIndex < amountOfCycles; cycleIndex++) {
+        if (mappingChangedPerCycle[cycleIndex]) {
+            minCycleWidths[cycleIndex] = amountOfColumns * columnWidth + (amountOfColumns + 1) * layout.getQubitSpacing();
+        }
+    }
+
+    // Load the fill colors for virtual qubits.
+    //TODO: implement better way of generating distinct colors for any given amount of qubits.
+    Vec<Color> virtualColors(amountOfQubits);
+    const Int division = 255 / amountOfQubits;
+    for (Int qubitIndex = 0; qubitIndex < amountOfQubits; qubitIndex++) {
+        const Int currentColor = division * qubitIndex;
+        const Int R = qubitIndex % 3 != 0 ? currentColor : 0;
+        const Int G = qubitIndex % 3 != 1 ? currentColor : 0;
+        const Int B = qubitIndex % 3 != 2 ? currentColor : 0;
+        const Color virtualColor = {{ R, G, B }};
+        virtualColors[qubitIndex] = virtualColor;
+    }
+
+    // Generate the image.
+    const Int extendedImageHeight = amountOfRows * rowHeight + (amountOfRows + 1) * layout.getQubitSpacing() + layout.getBorderSize();
+    ImageOutput imageOutput = generateImage(program, configuration, minCycleWidths, extendedImageHeight);
+
+    // Fill in cycle spaces beneath the circuit with the mapping graph.
+    const Int yStart = imageOutput.structure.getImageHeight() - extendedImageHeight;
+    const Int yEnd = imageOutput.structure.getImageHeight();
+
+    // Draw the mapping for each cycle.
+    for (Int cycleIndex = 0; cycleIndex < amountOfCycles; cycleIndex++) {
+        // Skip cycles for which the mapping has not changed.
+        if (!mappingChangedPerCycle[cycleIndex]) {
+            continue;
+        }
+
+        const Position4 position = imageOutput.structure.getCellPosition(cycleIndex, 0, QUANTUM);
+        const Int xStart = position.x0;
+        const Int xEnd = position.x1;
+
+        // Calculate the qubit positions.
+        Vec<Position2> qubitPositions(amountOfQubits, { 0, 0 });
+        for (Int qubitIndex = 0; qubitIndex < amountOfQubits; qubitIndex++) {
+            const Int column = parsedTopology ? topology.vertices[qubitIndex].x : qubitIndex % amountOfColumns;
+            const Int row = parsedTopology ? topology.ySize - 1 - topology.vertices[qubitIndex].y : qubitIndex / amountOfRows; // flip y-axis for topology
+            const Int centerX = xStart + column * columnWidth + (column + 1) * layout.getQubitSpacing() + layout.getQubitRadius();
+            const Int centerY = yStart + row * rowHeight + (row + 1) * layout.getQubitSpacing() + layout.getQubitRadius();
+            qubitPositions[qubitIndex].x = centerX;
+            qubitPositions[qubitIndex].y = centerY;
+        }
+
+        // Draw the edges.
+        for (const Edge edge : topology.edges) {
+            // Ignore qubits that are not present in the circuit.
+            if (edge.src >= amountOfQubits || edge.dst >= amountOfQubits) {
+                continue;
+            }
+            const Position2 src = qubitPositions[edge.src];
+            const Position2 dst = qubitPositions[edge.dst];
+            imageOutput.image.drawLine(src.x, src.y, dst.x, dst.y, black, 1.0f, LinePattern::UNBROKEN);
+        }
+
+        // Draw each of the qubit mappings in this cycle.
+        for (Int qubitIndex = 0; qubitIndex < amountOfQubits; qubitIndex++) {
+            const Position2 position = qubitPositions[qubitIndex];
+            const Int virtualOperand = virtualQubits[cycleIndex][qubitIndex];
+
+            // Draw qubit circle.
+            const Color virtualColor = virtualOperand != -1 ? virtualColors[virtualOperand] : layout.getQubitFillColor();
+            const Color fillColor = layout.getShowVirtualColors() && virtualOperand != -1 ? virtualColor : layout.getQubitFillColor();
+            imageOutput.image.drawFilledCircle(position.x, position.y, layout.getQubitRadius(), fillColor, 1.0f);
+            imageOutput.image.drawOutlinedCircle(position.x, position.y, layout.getQubitRadius(), layout.getQubitOutlineColor(), 1.0f, LinePattern::UNBROKEN);
+
+            // Draw real qubit index if enabled.
+            if (layout.getShowRealIndices()) {
+                const Str realIndexLabel = utils::to_string(qubitIndex);
+                const Dimensions realIndexLabelDimensions = calculateTextDimensions(realIndexLabel, layout.getFontHeightReal());
+
+                const Int realIndexLabelX = position.x - realIndexLabelDimensions.width / 2;
+                const Int realIndexLabelY = position.y - layout.getQubitRadius() - realIndexLabelDimensions.height - layout.getRealIndexSpacing();
+                imageOutput.image.drawText(realIndexLabelX, realIndexLabelY, realIndexLabel, layout.getFontHeightReal(), layout.getTextColorReal());
+            }
+
+            // Draw virtual operand label on qubit.
+            if (virtualOperand != -1) {
+                const Str text = utils::to_string(virtualOperand);
+
+                const Dimensions dimensions = calculateTextDimensions(text, layout.getFontHeightVirtual());
+                const Int textX = position.x - dimensions.width / 2;
+                const Int textY = position.y - dimensions.height / 2;
+                imageOutput.image.drawText(textX, textY, text, layout.getFontHeightVirtual(), layout.getTextColorVirtual());
+            }
+        }
+    }
+
+    // Save the image if enabled.
+    if (imageOutput.circuitLayout.saveImage) {
+        imageOutput.image.save(generateFilePath("circuit_visualization", "bmp"));
+    }
+
+    // Display the filled in image.
+    imageOutput.image.display("Mapping Graph");
+}
+
+void computeMappingPerCycle(const MappingGraphLayout layout,
+                            Vec<Vec<Int>> &virtualQubits,
+                            Vec<Bool> &mappingChangedPerCycle,
+                            const Vec<GateProperties> &gates,
+                            const Int amountOfCycles,
+                            const Int amountOfQubits) {
+    // Initialize the first cycle with a virtual index = real index mapping.
+    for (Int qubitIndex = 0; qubitIndex < amountOfQubits; qubitIndex++) {
+        const Int virtualIndex = layout.getInitDefaultVirtuals() ? qubitIndex : -1;
+        virtualQubits[0].push_back(virtualIndex);
+        mappingChangedPerCycle[0] = true; // the mapping has changed for the first cycle always!
+    }
+
+    struct SwapOperands {
+        Int r0;
+        Int r1;
+        Int v0;
+        Int v1;
+    };
+
+    // Find the swaps.
+    Vec<SwapOperands> swaps(amountOfCycles, {-1, -1, -1, -1});
+    Int startSearchFromCycle = 0;
+    for (const GateProperties &gate : gates) {
+        // Search the remaining cycles of the circuit for a swap.
+        if (gate.cycle >= startSearchFromCycle) {
+            // If a swap is found, search the rest of the circuit for more parts of the swap.
+            if (gate.swap_params.part_of_swap) {
+                const Int r0 = gate.swap_params.r0;
+                const Int r1 = gate.swap_params.r1;
+                const Int v0 = gate.swap_params.v0;
+                const Int v1 = gate.swap_params.v1;
+                Int nextPartOfSwapCycle = gate.cycle;
+                // Search for the cycle of the next part of the swap.
+                for (const GateProperties &gateInSearch : gates) {
+                    if (gateInSearch.swap_params.part_of_swap) {
+                        const Int nextV0 = gateInSearch.swap_params.v0;
+                        const Int nextV1 = gateInSearch.swap_params.v1;
+                        if (gateInSearch.cycle > nextPartOfSwapCycle) {
+                            if (v0 == nextV0 && v1 == nextV1) {
+                                nextPartOfSwapCycle = gateInSearch.cycle;
+                            }
+                        }
+                    }
+                }
+
+                // We now have a swap from gate.cycle to nextPartOfSwapCycle. Add it to the swap vector.
+                const SwapOperands swapOperands = {r0, r1, v0, v1};
+                swaps[nextPartOfSwapCycle] = swapOperands;
+                mappingChangedPerCycle[nextPartOfSwapCycle] = true;
+
+                // Continue the search from the next cycle.
+                startSearchFromCycle = nextPartOfSwapCycle + 1;
+            }
+        }
+    }
+
+    // Fill the cycles with qubit mappings.
+    for (Int cycleIndex = 1; cycleIndex < amountOfCycles; cycleIndex++) {
+        // Copy virtual qubit operand from the previous cycle of the same qubit.
+        for (Int qubitIndex = 0; qubitIndex < amountOfQubits; qubitIndex++) {
+            virtualQubits[cycleIndex].push_back(virtualQubits[cycleIndex - 1][qubitIndex]);
+        }
+        for (const GateProperties &gate : gates) {
+            if (gate.cycle == cycleIndex) {
+                for (Int qubitIndex = 0; qubitIndex < gate.operands.size(); qubitIndex++) {
+                    const Int mappingIndex = gate.operands[qubitIndex];
+                    const Int mappingIndexFromPreviousCycle = virtualQubits[cycleIndex - 1][mappingIndex];
+                    if (mappingIndexFromPreviousCycle == -1) {
+                        virtualQubits[cycleIndex][mappingIndex] = mappingIndex;
+                        mappingChangedPerCycle[cycleIndex] = true;
+                    }
+                }
+            }
+        }
+        // Check if there was a swap in this cycle.
+        const Int v0 = swaps[cycleIndex].v0;
+        const Int v1 = swaps[cycleIndex].v1;
+        const Bool swapInCycle = v0 != -1 && v1 != -1;
+        if (swapInCycle) {
+            const Int r0 = swaps[cycleIndex].r0;
+            const Int r1 = swaps[cycleIndex].r1;
+            virtualQubits[cycleIndex][r0] = v0;
+            virtualQubits[cycleIndex][r1] = v1;
+        }
+    }
+}
+
+Bool parseTopology(Json topologyJson, Topology &topology) {
+    const Str fallbackMessage = "Falling back on basic visualization. Missing attribute: ";
+    if (topologyJson.count("x_size") == 1) { topology.xSize = topologyJson["x_size"]; } else { QL_IOUT(fallbackMessage << "x_size"); return false; }
+    if (topologyJson.count("y_size") == 1) { topology.ySize = topologyJson["y_size"]; } else { QL_IOUT(fallbackMessage << "y_size"); return false; }
+
+    if (topologyJson.count("qubits") == 1) {
+        const Json qubits = topologyJson["qubits"];
+        topology.vertices.resize(qubits.size(), { 0, 0 });
+        for (const Json qubit : qubits) {
+            if (qubit.count("id") == 1 && qubit.count("x") == 1 && qubit.count("y") == 1) {
+                const Int id = qubit["id"];
+                const Int x = qubit["x"];
+                const Int y = qubit["y"];
+                topology.vertices[id].x = x;
+                topology.vertices[id].y = y;
+            }
+            else {
+                QL_IOUT(fallbackMessage << "id, or x or y");
+                return false;
+            }
+        }
+    } else {
+        QL_IOUT(fallbackMessage << " qubits");
+        return false;
+    }
+
+    if (topologyJson.count("edges") == 1) {
+        const Json edges = topologyJson["edges"];
+        topology.edges.resize(edges.size(), { 0, 0 });
+        for (const Json edge : edges) {
+            if (edge.count("id") == 1 && edge.count("src") == 1 && edge.count("dst") == 1) {
+                const Int id = edge["id"];
+                topology.edges[id].src = edge["src"];
+                topology.edges[id].dst = edge["dst"];
+            } else {
+                QL_IOUT(fallbackMessage << " id, or src or dst");
+                return false;
+            }
+        }
+    } else {
+        QL_IOUT(fallbackMessage << " edges");
+        return false;
+    }
+
+    return true;
+}
+
+MappingGraphLayout parseMappingGraphLayout(const Str &configPath) {
+    QL_DOUT("Parsing visualizer configuration file for mapping graph visualization...");
+
+    Json fullConfig;
+    try {
+        fullConfig = load_json(configPath);
+    } catch (Json::exception &e) {
+        QL_FATAL("Failed to load the visualization config file: \n\t" << std::string(e.what()));
+    }
+
+    Json config;
+    if (fullConfig.count("mappingGraph") == 1) {
+        config = fullConfig["mappingGraph"];
+    } else {
+        QL_WOUT("Could not find mapping graph configuration in visualizer configuration file. Is it named correctly?");
+    }
+
+    Json gridConfig;
+    if (fullConfig.count("circuit") == 1) {
+        Json circuitConfig = fullConfig["circuit"];
+        if (circuitConfig.count("grid") == 1) {
+            gridConfig = circuitConfig["grid"];
+        } else {
+            QL_WOUT("Could not find grid configuration in visualizer configuration file. Is it named correctly?");
+        }
+    }
+
+    // Fill the layout object with the values from the config file. Any missing values will assume the default values hardcoded in the layout object.
+    MappingGraphLayout layout;
+
+    // Check if the image should be saved to disk.
+    if (fullConfig.count("saveImage") == 1) {
+        layout.saveImage = fullConfig["saveImage"];
+    }
+
+    // Load the parameters.
+    if (config.count("initDefaultVirtuals") == 1)   layout.setInitDefaultVirtuals(config["initDefaultVirtuals"]);
+    if (config.count("showVirtualColors") == 1)   layout.setShowVirtualColors(config["showVirtualColors"]);
+    if (config.count("showRealIndices") == 1)   layout.setShowRealIndices(config["showRealIndices"]);
+    if (config.count("useTopology") == 1)       layout.setUseTopology(config["useTopology"]);
+
+    if (config.count("qubitRadius") == 1)       layout.setQubitRadius(config["qubitRadius"]);
+    if (config.count("qubitSpacing") == 1)      layout.setQubitSpacing(config["qubitSpacing"]);
+    if (config.count("fontHeightReal") == 1)      layout.setFontHeightReal(config["fontHeightReal"]);
+    if (config.count("fontHeightVirtual") == 1)      layout.setFontHeightVirtual(config["fontHeightVirtual"]);
+
+    if (config.count("textColorReal") == 1)      layout.setTextColorReal(config["textColorReal"]);
+    if (config.count("textColorVirtual") == 1)      layout.setTextColorVirtual(config["textColorVirtual"]);
+    if (config.count("qubitFillColor") == 1)      layout.setQubitFillColor(config["qubitFillColor"]);
+    if (config.count("qubitOutlineColor") == 1)      layout.setQubitOutlineColor(config["qubitOutlineColor"]);
+
+    if (config.count("realIndexSpacing") == 1)      layout.setRealIndexSpacing(config["realIndexSpacing"]);
+
+    if (gridConfig.count("borderSize") == 1)    layout.setBorderSize(gridConfig["borderSize"]);
+
+    return layout;
+}
+
+} // namespace ql
+
+#endif //WITH_VISUALIZER
\ No newline at end of file
diff --git a/src/visualizer_mapping.h b/src/visualizer_mapping.h
new file mode 100644
index 000000000..dc1814187
--- /dev/null
+++ b/src/visualizer_mapping.h
@@ -0,0 +1,45 @@
+/** \file
+ * Declaration of the visualizer mapping graph.
+ */
+
+#pragma once
+
+#ifdef WITH_VISUALIZER
+
+#include "visualizer.h"
+#include "visualizer_types.h"
+#include "utils/vec.h"
+#include "utils/json.h"
+#include "utils/num.h"
+
+namespace ql {
+
+struct Edge {
+    utils::Int src;
+    utils::Int dst;
+
+    Edge() = delete;
+};
+
+struct Topology {
+    utils::Int xSize = 0;
+    utils::Int ySize = 0;
+    utils::Vec<Position2> vertices;
+    utils::Vec<Edge> edges;
+};
+
+void visualizeMappingGraph(const quantum_program* program, const VisualizerConfiguration &configuration);
+
+void computeMappingPerCycle(const MappingGraphLayout layout,
+                            utils::Vec<utils::Vec<utils::Int>> &virtualQubits,
+                            utils::Vec<utils::Bool> &mappingChangedPerCycle,
+                            const utils::Vec<GateProperties> &gates,
+                            const utils::Int amountOfCycles,
+                            const utils::Int amountOfQubits);
+
+utils::Bool parseTopology(const utils::Json hardware_settings, Topology &topology);
+MappingGraphLayout parseMappingGraphLayout(const utils::Str &configPath);
+
+} // namespace ql
+
+#endif //WITH_VISUALIZER
\ No newline at end of file
diff --git a/src/visualizer_types.h b/src/visualizer_types.h
index 22e876ee7..9135b1810 100644
--- a/src/visualizer_types.h
+++ b/src/visualizer_types.h
@@ -98,18 +98,69 @@ struct GateProperties {
     utils::Str name;
     utils::Vec<utils::Int> operands;
     utils::Vec<utils::Int> creg_operands;
+    swap_parameters swap_params;
     utils::Int duration;
     utils::Int cycle;
     gate_type_t type;
     utils::Vec<utils::Int> codewords; // std::vector<size_t> codewords; // index 0 is right and index 1 is left, in case of multi-qubit gate
     utils::Str visual_type;
-    utils::Int virtual_qubit_index; // only used for remap gates
 
     GateProperties() = delete;
 };
 
 // ------------- Layout declaration -------------- //
 
+class MappingGraphLayout {
+private:
+    utils::Bool initDefaultVirtuals = false;
+    utils::Bool showVirtualColors = true;
+    utils::Bool showRealIndices = true;
+    utils::Bool useTopology = true;
+    utils::Int qubitRadius = 15;
+    utils::Int qubitSpacing = 7;
+    utils::Int borderSize = 32;
+    utils::Int fontHeightReal = 13;
+    utils::Int fontHeightVirtual = 13;
+    Color textColorReal = black;
+    Color textColorVirtual = black;
+    utils::Int realIndexSpacing = 3;
+    Color qubitFillColor = white;
+    Color qubitOutlineColor = black;
+
+public:
+    utils::Bool saveImage = false;
+
+    utils::Bool getInitDefaultVirtuals() const { return initDefaultVirtuals; }
+    utils::Bool getShowVirtualColors() const { return showVirtualColors; }
+    utils::Bool getShowRealIndices() const { return showRealIndices; }
+    utils::Bool getUseTopology() const { return useTopology; }
+    utils::Int getQubitRadius() const { return qubitRadius; }
+    utils::Int getQubitSpacing() const { return qubitSpacing; }
+    utils::Int getBorderSize() const { return borderSize; }
+    utils::Int getFontHeightReal() const { return fontHeightReal; }
+    utils::Int getFontHeightVirtual() const { return fontHeightVirtual; }
+    Color getTextColorReal() const { return textColorReal; }
+    Color getTextColorVirtual() const { return textColorVirtual; }
+    utils::Int getRealIndexSpacing() const { return realIndexSpacing; }
+    Color getQubitFillColor() const { return qubitFillColor; }
+    Color getQubitOutlineColor() const { return qubitOutlineColor; }
+
+    void setInitDefaultVirtuals(const utils::Bool argument) { initDefaultVirtuals = argument; }
+    void setShowVirtualColors(const utils::Bool argument) { showVirtualColors = argument; }
+    void setShowRealIndices(const utils::Bool argument) { showRealIndices = argument; }
+    void setUseTopology(const utils::Bool argument) { useTopology = argument; }
+    void setQubitRadius(const utils::Int argument) { assertPositive(argument, "qubitRadius"); qubitRadius = argument; }
+    void setQubitSpacing(const utils::Int argument) { assertPositive(argument, "qubitSpacing"); qubitSpacing = argument; }
+    void setBorderSize(const utils::Int argument) { assertPositive(argument, "borderSize"); borderSize = argument; }
+    void setFontHeightReal(const utils::Int argument) { assertPositive(argument, "fontHeightReal"); fontHeightReal = argument; }
+    void setFontHeightVirtual(const utils::Int argument) { assertPositive(argument, "fontHeightVirtual"); fontHeightVirtual = argument; }
+    void setTextColorReal(const Color argument) { textColorReal = argument; }
+    void setTextColorVirtual(const Color argument) { textColorVirtual = argument; }
+    void setRealIndexSpacing(const utils::Int argument) { assertPositive(argument, "realIndexSpacing"); realIndexSpacing = argument; }
+    void setQubitFillColor(const Color argument) { qubitFillColor = argument; }
+    void setQubitOutlineColor(const Color argument) { qubitOutlineColor = argument; }
+};
+
 class InteractionGraphLayout {
 private:
     utils::Bool outputDotFile = false;
@@ -403,6 +454,7 @@ struct Pulses {
 
 struct CircuitLayout {
     utils::Bool saveImage = false;
+    Color backgroundColor = {{ 0, 0, 50 }};
 
     Cycles cycles;
     BitLines bitLines;


Test patch:
diff --git a/tests/visualizer/test_remap_gate.json b/tests/visualizer/test_s7.json
similarity index 90%
rename from tests/visualizer/test_remap_gate.json
rename to tests/visualizer/test_s7.json
index 5f2915e3b..40e6b354a 100644
--- a/tests/visualizer/test_remap_gate.json
+++ b/tests/visualizer/test_s7.json
@@ -153,7 +153,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "prepx",
-            "cc_light_opcode": 1
+            "cc_light_opcode": 1,
+            "visual_type": "prepx"
         },
         "prepz": {
             "duration": 620,
@@ -163,7 +164,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "prepz",
-            "cc_light_opcode": 2
+            "cc_light_opcode": 2,
+            "visual_type": "prepz"
         },
         "measx_keep": {
             "duration": 340,
@@ -173,7 +175,8 @@
             "type": "readout",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "measx",
-            "cc_light_opcode": 4
+            "cc_light_opcode": 4,
+            "visual_type": "measure"
         },
         "measz_keep": {
             "duration": 300,
@@ -183,7 +186,8 @@
             "type": "readout",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "measz",
-            "cc_light_opcode": 4
+            "cc_light_opcode": 4,
+            "visual_type": "measure"
         },
         "measure": {
             "duration": 300,
@@ -193,7 +197,8 @@
             "type": "readout",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "measz",
-            "cc_light_opcode": 4
+            "cc_light_opcode": 4,
+            "visual_type": "measure"
         },
         "i": {
             "duration": 20,
@@ -203,7 +208,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "i",
-            "cc_light_opcode": 5
+            "cc_light_opcode": 5,
+            "visual_type": "i"
         },
         "x": {
             "duration": 20,
@@ -213,7 +219,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "x",
-            "cc_light_opcode": 6
+            "cc_light_opcode": 6,
+            "visual_type": "x"
         },
         "y": {
             "duration": 20,
@@ -223,7 +230,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "y",
-            "cc_light_opcode": 7
+            "cc_light_opcode": 7,
+            "visual_type": "y"
         },
         "z": {
             "duration": 40,
@@ -233,7 +241,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "z",
-            "cc_light_opcode": 8
+            "cc_light_opcode": 8,
+            "visual_type": "z"
         },
         "rx": {
             "duration": 60,
@@ -243,7 +252,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "rx",
-            "cc_light_opcode": 6
+            "cc_light_opcode": 6,
+            "visual_type": "rx"
         },
         "ry": {
             "duration": 60,
@@ -253,7 +263,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "ry",
-            "cc_light_opcode": 7
+            "cc_light_opcode": 7,
+            "visual_type": "ry"
         },
         "rz": {
             "duration": 60,
@@ -263,7 +274,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "rz",
-            "cc_light_opcode": 8
+            "cc_light_opcode": 8,
+            "visual_type": "rz"
         },
         "h": {
             "duration": 40,
@@ -273,7 +285,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "h",
-            "cc_light_opcode": 9
+            "cc_light_opcode": 9,
+            "visual_type": "h"
         },
         "s": {
             "duration": 60,
@@ -283,7 +296,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "s",
-            "cc_light_opcode": 10
+            "cc_light_opcode": 10,
+            "visual_type": "s"
         },
         "sdag": {
             "duration": 60,
@@ -293,7 +307,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "sdag",
-            "cc_light_opcode": 11
+            "cc_light_opcode": 11,
+            "visual_type": "sdag"
         },
         "x90": {
             "duration": 20,
@@ -303,7 +318,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "x90",
-            "cc_light_opcode": 12
+            "cc_light_opcode": 12,
+            "visual_type": "x90"
         },
         "xm90": {
             "duration": 20,
@@ -313,7 +329,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "xm90",
-            "cc_light_opcode": 13
+            "cc_light_opcode": 13,
+            "visual_type": "xm90"
         },
         "y90": {
             "duration": 20,
@@ -323,7 +340,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "y90",
-            "cc_light_opcode": 14
+            "cc_light_opcode": 14,
+            "visual_type": "y90"
         },
         "ym90": {
             "duration": 20,
@@ -333,7 +351,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "ym90",
-            "cc_light_opcode": 15
+            "cc_light_opcode": 15,
+            "visual_type": "ym90"
         },
         "t": {
             "duration": 60,
@@ -343,7 +362,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "t",
-            "cc_light_opcode": 16
+            "cc_light_opcode": 16,
+            "visual_type": "t"
         },
         "tdag": {
             "duration": 60,
@@ -353,7 +373,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "tdag",
-            "cc_light_opcode": 17
+            "cc_light_opcode": 17,
+            "visual_type": "tdag"
         },
         "x45": {
             "duration": 20,
@@ -363,7 +384,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "x45",
-            "cc_light_opcode": 18
+            "cc_light_opcode": 18,
+            "visual_type": "x45"
         },
         "xm45": {
             "duration": 20,
@@ -373,7 +395,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "xm45",
-            "cc_light_opcode": 19
+            "cc_light_opcode": 19,
+            "visual_type": "xm45"
         },
         "y45": {
             "duration": 20,
@@ -383,7 +406,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "y45",
-            "cc_light_opcode": 18
+            "cc_light_opcode": 18,
+            "visual_type": "y45"
         },
         "ym45": {
             "duration": 20,
@@ -393,7 +417,8 @@
             "type": "mw",
             "cc_light_instr_type": "single_qubit_gate",
             "cc_light_instr": "ym45",
-            "cc_light_opcode": 19
+            "cc_light_opcode": 19,
+            "visual_type": "ym45"
         },
         "cz": {
             "duration": 40,
@@ -403,7 +428,8 @@
             "type": "flux",
             "cc_light_instr_type": "two_qubit_gate",
             "cc_light_instr": "cz",
-            "cc_light_opcode": 129
+            "cc_light_opcode": 129,
+            "visual_type": "cz"
         },
         "cnot": {
             "duration": 80,
@@ -413,7 +439,8 @@
             "type": "flux",
             "cc_light_instr_type": "two_qubit_gate",
             "cc_light_instr": "cnot",
-            "cc_light_opcode": 128
+            "cc_light_opcode": 128,
+            "visual_type": "cnot"
         },
         "swap": {
             "duration": 200,
@@ -423,7 +450,8 @@
             "type": "flux",
             "cc_light_instr_type": "two_qubit_gate",
             "cc_light_instr": "swap",
-            "cc_light_opcode": 128
+            "cc_light_opcode": 128,
+            "visual_type": "swap"
         },
         "move": {
             "duration": 140,
@@ -433,7 +461,8 @@
             "type": "flux",
             "cc_light_instr_type": "two_qubit_gate",
             "cc_light_instr": "move",
-            "cc_light_opcode": 128
+            "cc_light_opcode": 128,
+            "visual_type": "move"
         }
     },
 
diff --git a/tests/visualizer/visualizer_config_example1.json b/tests/visualizer/visualizer_config_example1.json
index fc14c9ced..19a8958d6 100644
--- a/tests/visualizer/visualizer_config_example1.json
+++ b/tests/visualizer/visualizer_config_example1.json
@@ -1,5 +1,22 @@
 {
-    "saveImage": false,
+    "saveImage": true,
+    "backgroundColor": [179, 235, 255],
+    "mappingGraph":
+    {
+        "initDefaultVirtuals": false,
+        "showVirtualColors": true,
+        "showRealIndices": true,
+        "useTopology": true,
+        "qubitRadius": 15,
+        "qubitSpacing": 7,
+        "fontHeightReal": 13,
+        "fontHeightVirtual": 13,
+        "textColorReal": [0, 0, 255],
+        "textColorVirtual": [255, 0, 0],
+        "realIndexSpacing": 1,
+        "qubitFillColor": [255, 255, 255],
+        "qubitOutlineColor": [0, 0, 0]
+    },
     "interactionGraph":
     {
         "outputDotFile": true,
@@ -129,6 +146,20 @@
                     }
                 ]
             },
+            "y": {
+                "connectionColor": [0, 0, 0],
+                "nodes": [
+                    {
+                        "type": "GATE",
+                        "radius": 13,
+                        "displayName": "y",
+                        "fontHeight": 13,
+                        "fontColor": [255, 255, 255],
+                        "backgroundColor": [70, 210, 230],
+                        "outlineColor": [70, 210, 230]
+                    }
+                ]
+            },
             "h": {
                 "connectionColor": [0, 0, 0],
                 "nodes": [
@@ -143,6 +174,20 @@
                     }
                 ]
             },
+            "y90": {
+                "connectionColor": [0, 0, 0],
+                "nodes": [
+                    {
+                        "type": "GATE",
+                        "radius": 13,
+                        "displayName": "y90",
+                        "fontHeight": 13,
+                        "fontColor": [255, 255, 255],
+                        "backgroundColor": [70, 210, 230],
+                        "outlineColor": [70, 210, 230]
+                    }
+                ]
+            },
             "ry90": {
                 "connectionColor": [0, 0, 0],
                 "nodes": [
@@ -157,6 +202,20 @@
                     }
                 ]
             },
+            "ym90": {
+                "connectionColor": [0, 0, 0],
+                "nodes": [
+                    {
+                        "type": "GATE",
+                        "radius": 13,
+                        "displayName": "ym90",
+                        "fontHeight": 13,
+                        "fontColor": [255, 255, 255],
+                        "backgroundColor": [70, 210, 230],
+                        "outlineColor": [70, 210, 230]
+                    }
+                ]
+            },
             "cz": {
                 "connectionColor": [0, 0, 0],
                 "nodes": [
diff --git a/tests/visualizer/visualizer_example1.py b/tests/visualizer/visualizer_example1.py
index 0fb46413f..685d185e3 100644
--- a/tests/visualizer/visualizer_example1.py
+++ b/tests/visualizer/visualizer_example1.py
@@ -23,58 +23,32 @@
 ql.set_option('mapselectmaxwidth', 'min')
 ql.set_option('use_default_gates', 'yes')
 
-# ql.set_option('log_level', 'LOG_DEBUG')
 ql.set_option('log_level', 'LOG_INFO')
 ql.set_option('unique_output', 'yes')
 ql.set_option('write_qasm_files', 'no')
 ql.set_option('write_report_files', 'no')
 
 c = ql.Compiler("testCompiler")
-# c.add_pass("RotationOptimizer")
-# c.add_pass("DecomposeToffoli")
 c.add_pass("BackendCompiler")
 c.add_pass("Visualizer")
 
-# c.set_pass_option("BackendCompiler", "eqasm_compiler_name", "cc_light_compiler")
 c.set_pass_option("ALL", "skip", "no")
 c.set_pass_option("ALL", "write_qasm_files", "no")
 c.set_pass_option("ALL", "write_report_files", "no")
-#c.set_pass_option("Visualizer", "visualizer_type", "INTERACTION_GRAPH")
-c.set_pass_option("Visualizer", "visualizer_type", "CIRCUIT")
+c.set_pass_option("Visualizer", "visualizer_type", "MAPPING_GRAPH")
 c.set_pass_option("Visualizer", "visualizer_config_path", os.path.join(curdir, "visualizer_config_example1.json"))
 c.set_pass_option("Visualizer", "visualizer_waveform_mapping_path", os.path.join(curdir, "waveform_mapping.json"))
 
-# platformCustomGates = ql.Platform('starmon', os.path.join(curdir, 'hardware_config_cc_light_visualizer.json'))
-platformCustomGates = ql.Platform('starmon', os.path.join(curdir, 'test_remap_gate.json'))
+platformCustomGates = ql.Platform('starmon', os.path.join(curdir, 'test_s7.json'))
 nqubits = 7
 p = ql.Program("testProgram1", platformCustomGates, nqubits, 0)
 k = ql.Kernel("aKernel1", platformCustomGates, nqubits, 0)
 
 k.gate("x", [2])
-k.gate("x", [3])
+k.gate("y", [3])
 k.gate("cnot", [2,3])
 k.gate("x", [2])
-k.gate("x", [3])
+k.gate("y", [3])
 
 p.add_kernel(k)
-c.compile(p)
-
-# k.gate('x', [0])
-# for i in range(nqubits):
-#     k.gate('prepz', [i])
-# k.gate('wait', [1], 40)
-# k.gate('wait', [2], 40)
-# k.gate('wait', [3], 40)
-# k.gate('x', [0])
-# k.gate('x', [0])
-# k.gate('x', [0])
-# k.gate('wait', [2], 40)
-# k.gate('h', [2])
-# k.gate('cz', [3, 1])
-# k.gate('cz', [2, 0])
-# k.gate('cz', [2, 0])
-# k.gate('wait', [3], 40)
-# k.gate('measure', [3])
-# k.gate('measure', [0])
-# k.gate('measure', [1])
-# k.gate('measure', [2])
\ No newline at end of file
+c.compile(p)
\ No newline at end of file
diff --git a/tests/visualizer/visualizer_example6.py b/tests/visualizer/visualizer_example6.py
new file mode 100644
index 000000000..e55566102
--- /dev/null
+++ b/tests/visualizer/visualizer_example6.py
@@ -0,0 +1,54 @@
+from openql import openql as ql
+import os
+
+curdir = os.path.dirname(__file__)
+output_dir = os.path.join(curdir, 'visualizer_example_output')
+
+ql.set_option('output_dir', output_dir)
+ql.set_option('optimize', 'no')
+ql.set_option('scheduler', 'ASAP')
+
+ql.set_option('clifford_premapper', 'yes')
+ql.set_option('clifford_postmapper', 'yes')
+ql.set_option('mapper', 'minextendrc')
+ql.set_option('mapinitone2one', 'yes')
+ql.set_option('initialplace', 'no')
+ql.set_option('initialplace2qhorizon', '0')
+ql.set_option('mapusemoves', 'yes')
+ql.set_option('mapreverseswap', 'yes')
+ql.set_option('mappathselect', 'all')
+ql.set_option('maplookahead', 'noroutingfirst')
+ql.set_option('maprecNN2q', 'no')
+ql.set_option('mapselectmaxlevel', '0')
+ql.set_option('mapselectmaxwidth', 'min')
+ql.set_option('use_default_gates', 'yes')
+
+ql.set_option('log_level', 'LOG_INFO')
+ql.set_option('unique_output', 'yes')
+ql.set_option('write_qasm_files', 'no')
+ql.set_option('write_report_files', 'no')
+
+c = ql.Compiler("testCompiler")
+c.add_pass("BackendCompiler")
+c.add_pass("Visualizer")
+
+c.set_pass_option("ALL", "skip", "no")
+c.set_pass_option("ALL", "write_qasm_files", "no")
+c.set_pass_option("ALL", "write_report_files", "no")
+c.set_pass_option("Visualizer", "visualizer_type", "MAPPING_GRAPH")
+c.set_pass_option("Visualizer", "visualizer_config_path", os.path.join(curdir, "visualizer_config_example1.json"))
+c.set_pass_option("Visualizer", "visualizer_waveform_mapping_path", os.path.join(curdir, "waveform_mapping.json"))
+
+platformCustomGates = ql.Platform('starmon', os.path.join(curdir, 'test_s7.json'))
+nqubits = 7
+p = ql.Program("testProgram1", platformCustomGates, nqubits, 0)
+k = ql.Kernel("aKernel1", platformCustomGates, nqubits, 0)
+
+k.gate("x", [2])
+k.gate("y", [4])
+k.gate("cnot", [2,4])
+k.gate("x", [2])
+k.gate("y", [4])
+
+p.add_kernel(k)
+c.compile(p)
\ No newline at end of file