Quantum Superposition in Musical Representation: Beethoven's Sonata on a Quantum Keyboard

In this experiment using Qiskit, we reinterpret Beethoven's "Moonlight Sonata" (1st Movement) through quantum physics. By entangling qubits to correlate melody and harmony and employing superposition to generate chords, we create a quantum-generated version of the piece. Each note's state remains probabilistic until actual quantum measurement, ensuring that every rendition of the sonata is distinct and influenced by the randomness of quantum mechanics. The resulting music offers a novel listening experience that embodies the unpredictability of quantum superposition.

Code Walkthrough
1. Setup and Initialization:
We begin by creating a quantum circuit that comprises 88 qubits, each representing a potential musical note, and additional qubits dedicated to chords. In quantum mechanics, the state of a qubit can be represented as a vector in a two-dimensional complex Hilbert space, typically denoted as ∣0⟩ (no note played) and ∣1⟩ (note played).

2. Frequency Assignment:
Each qubit is mapped to a frequency that corresponds to a note on the piano. The relationship between the frequency f of a note and its position n on the piano is given by the equation for the equal temperament scale:
f = f_0 * 2^(n/12)
where f_0 is the frequency of a reference note, usually A4 with 0 = 440 Hz.

3. Quantum Entanglement:
For each note in the melody and corresponding harmony, we entangle two qubits. The Hadamard gate (H) is applied to a qubit to create an equal superposition state:
H∣0⟩ = 1/sqrt(2) * (∣0⟩+∣1⟩)
Then, a controlled-NOT (CNOT) gate entangles the qubit with another, creating a pair where the state of one qubit will directly affect the state of the other. The CNOT gate's action on a pair of qubits can be described as:
CNOT∣q_1, q_2⟩ = ∣q_1, q_1 ⊕ q_2⟩
where ⊕ denotes addition modulo 2.

4. Chord Superposition:
To represent chords, we use a set of qubits and place them into a superposition of states that correspond to different chords. Applying the Hadamard gate to each of these qubits results in a superposition that encompasses all possible chords. For a system of m qubits, this can be represented as:
H^(⊗m) ∣0...0⟩ = 1/sqrt(2^m) * ((2^m)-1 ∑ x=0) ∣x⟩
where H⊗m denotes the tensor product of m Hadamard gates, and ∣x⟩ represents each possible state of the chord qubits.

5. Quantum Measurement:
When we measure the quantum circuit, the superposition collapses to a specific state based on the probability distribution given by the wavefunction's amplitude squared. The measurement of a qubit in state α∣0⟩ + β∣1⟩ will yield ∣0⟩ with probability ∣α∣^2 and ∣1⟩ with probability ∣β∣^2, where ∣α∣^2 + ∣β∣^2 = 1.

6. Sound Synthesis:
The measured state is then converted to sound. The frequency of each note is determined by the qubit state and its corresponding frequency mapping. The sound wave for each note can be described by the sine wave equation:
y(t) = Asin(2πft + ϕ)
where A is the amplitude, f is the frequency, t is time, and ϕ is the phase.

7. Digital Conversion:
The analog sound wave is sampled at a certain rate and quantized to be stored digitally. This process uses analog-to-digital conversion. The data is also saved into a json for further analysis.

8. Playback:
The final step is playing back the digitally encoded music, which is now a unique performance of the "Moonlight Sonata" as determined by the quantum process, reflecting both the notation of Beethoven's composition and the indeterminacy of quantum physics. Will work on smoother sine waves but love how dubstep the sine wave sounds here.

Results:
Listen to the song here: https://x.com/ComancheTippie/status/1720226587336184030 🎶

Json Analysis:
Binary Representation:
Each binary string represents the state of 88 qubits. A 0 indicates that the qubit was measured in the |0⟩ state, and a 1 indicates the |1⟩ state. The pattern of zeros and ones is directly related to the notes played in the quantum musical piece – each 1 represents a note being played.

There are no states with a very high frequency of occurrence, which implies that there isn't a dominant state in this quantum system. This is consistent with a quantum system that has a high degree of superposition and entanglement, leading to a wide spread of possible outcomes.

In the end, we created music that used quantum entanglement and superposition to generate a version of Beethoven's "Moonlight Sonata". The resulting sound, converted into an MP3 file, was a direct auditory representation of quantum states. Analysis of the quantum measurements showed a uniform distribution, indicating that our quantum musical composition played every note with equal probability, mirroring the uncertainty of quantum superposition.”

Description of Image 1

Code:

import numpy as np
from qiskit import QuantumRegister, QuantumCircuit, Aer, execute, transpile
import lameenc
import json

## Constants
qubits = 88
chord_qubits = 4  # 4 qubits for chords, allowing 16 possible chords
sample_rate = 48000 # sample rate
duration = 0.5 # Adjust the duration of the notes
harmony_volume = 0.011  # Adjust the volume of the harmony

# Frequencies for all notes on a standard piano (A0 to C8)
frequencies = [
    27.50, 29.14, 30.87, 32.70, 34.65, 36.71, 38.89, 41.20, 43.65, 46.25, 48.99, 51.91,
    55.00, 58.27, 61.74, 65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.50, 97.99, 103.83,
    110.00, 116.54, 123.47, 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 195.99, 207.65,
    220.00, 233.08, 246.94, 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 391.99, 415.30,
    440.00, 466.16, 493.88, 523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 739.99, 783.99, 830.61,
    880.00, 932.33, 987.77, 1046.50, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, 1661.22,
    1760.00, 1864.66, 1975.53, 2093.00, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44,
    3520.00, 3729.31, 3951.07, 4186.01
]

melody = [
    52, -1, 52, 55, 59, 55, 52, -1, 52, 55, 59, 55, 52, -1,  # Opening arpeggios
    52, 60, 59, 55, 52, 64, 62, 59, 55, -1, 52, -1,           # First theme
    57, -1, 57, 60, 64, 60, 57, -1, 57, 60, 64, 60, 57, -1,  # Transition
    59, 67, 66, 62, 59, 69, 67, 64, 60, -1, 59, -1,           # Second theme
    # Repeat themes with variations as needed
]

harmony = [
    40, -1, 40, 45, 48, 45, 40, -1, 40, 45, 48, 45, 40, -1,  # Harmonic base for arpeggios
    40, 48, 48, 45, 40, 52, 52, 48, 45, -1, 40, -1,           # Harmonic support for first theme
    45, -1, 45, 48, 52, 48, 45, -1, 45, 48, 52, 48, 45, -1,  # Harmonic base for transition
    47, 55, 55, 52, 47, 56, 56, 52, 48, -1, 47, -1,           # Harmonic support for second theme
    # Repeat themes with harmonies as needed
]

# Quantum Registers and Circuits for melody, harmony, and chords
qr_melody = QuantumRegister(qubits)
qr_harmony = QuantumRegister(qubits)
qr_chords = QuantumRegister(chord_qubits)
quantum_keyboard_circuit = QuantumCircuit(qr_melody, qr_harmony, qr_chords)

# Function to entangle melody and harmony notes
def entangle_notes(quantum_circuit, melody_qubit, harmony_qubit):
    quantum_circuit.h(melody_qubit)
    quantum_circuit.cx(melody_qubit, harmony_qubit)

# Function to create a chord superposition
def create_chord_superposition(quantum_circuit, chord_qubits):
    for qubit in chord_qubits:
        quantum_circuit.h(qubit)

# Entangle melody and harmony notes, and create a chord superposition
for i, (melody_note, harmony_note) in enumerate(zip(melody, harmony)):
    if melody_note != -1:
        entangle_notes(quantum_keyboard_circuit, qr_melody[melody_note], qr_harmony[harmony_note])
    if i < len(qr_chords):  # Creating chord superposition only for the first few notes
        create_chord_superposition(quantum_keyboard_circuit, [qr_chords[i]])

# Measure the qubits
quantum_keyboard_circuit.measure_all()

# Run the simulation
simulator = Aer.get_backend('aer_simulator')
compiled_circuit = transpile(quantum_keyboard_circuit, simulator)
result = simulator. run(compiled_circuit).result()

# Extracting the results
counts = result.get_counts(quantum_keyboard_circuit)
print("Quantum Measurement Results:", counts)

# Initialize MP3 Encoder
encoder = lameenc.Encoder()
encoder.set_bit_rate(128)
encoder.set_in_sample_rate(sample_rate)
encoder.set_channels(1)
encoder.set_quality(2)  # 2-highest, 7-fastest

# Generate sound based on the results, and write to MP3
mp3_filename = 'save loaction'
with open(mp3_filename, 'wb') as mp3_file:
    for i, (note_melody, note_harmony) in enumerate(zip(melody, harmony)):
        # Select a random outcome for the chord to simulate the collapse of the superposition
        chord_index = np.random.choice(2 ** chord_qubits)
        frequency_chord = frequencies[chord_index % len(frequencies)]
        
        # Generate samples for melody, harmony, and chord
        samples = np.zeros(int(sample_rate * duration)).astype(np.int16)
        if note_melody != -1:
            frequency_melody = frequencies[note_melody]
            samples += (np.sin(2 * np.pi * np.arange(sample_rate * duration) * frequency_melody / sample_rate) * 32767).astype(np.int16)
        if note_harmony != -1:
            frequency_harmony = frequencies[note_harmony]
            samples += (np.sin(2 * np.pi * np.arange(sample_rate * duration) * frequency_harmony / sample_rate) * 32767 * harmony_volume).astype(np.int16)
        samples += (np.sin(2 * np.pi * np.arange(sample_rate * duration) * frequency_chord / sample_rate) * 32767 * harmony_volume).astype(np.int16)
        
        # Apply a volume envelope (not implemented in this snippet)
        # samples = apply_volume_envelope(samples)

        # Encode to MP3 and write to file
        mp3_data = encoder.encode(samples.tobytes())
        mp3_file.write(mp3_data)
    mp3_file.write(encoder.flush())

print("Quantum melody with harmony generated and saved as", mp3_filename)

# Function to save results to JSON
def save_results_to_json(results, filename):
    with open(filename, 'w') as json_file:
        json.dump(results, json_file, indent=4)

# Correct the path to match your Windows directory structure
results_json_filename = 'save address'
save_results_to_json(counts, results_json_filename)

print(f"Results saved to {results_json_filename}")