<jupyter_start><jupyter_text>What is Quantum?<jupyter_code># This code is to create the interactive figure
from bokeh.events import ButtonClick
from bokeh.layouts import row, column
from bokeh.models import ColumnDataSource, CustomJS, Button, Slider, DataRange1d, LabelSet, RadioButtonGroup, Div
from bokeh.plotting import figure
from bokeh.embed import file_html
from bokeh.resources import CDN
from bokeh.io import output_notebook, show, curdoc
import IPython<jupyter_output><empty_output><jupyter_text>‘Quantum physics’ is a term widely used but much less understood. It is a mathematical model first used to describe the behavior of small things in a laboratory, which exposed gaps in the preceding theory of ‘classical’ physics. Quantum theory explains this behavior and gives us a more complete picture of our universe. We have realized we can use this previously unexplained behavior to perform certain computations that we previously did not believe possible. We call this quantum computing.Quantum computing is the perfect way to dip your toes into quantum physics. It distills the core concepts from quantum physics into their simplest forms, stripping away the complications of the physical world. This page will take you on a short journey to discover (and explain!) some strange quantum phenomena, and give you a taste for what ‘quantum’ is.   Review of Classical ProbabilityTo cover quantum phenomena, we need to first remind ourselves of 'classical' probabilities. In this sense, 'classical' just means pre-quantum, i.e. the normal probability trees you should have seen in school. If you're already familiar with this material, you should move through it quickly. If you're not so hot on this then don't worry- we'll only cover some of the simplest probability problems possible. Probability TreesYou will hopefully remember probability trees from school. The idea is simple - we use a drawing to map out every possible eventuality and from this, we can calculate the chance of it happening.Say we have a coin, and to start, we place it in the state Heads. If we then toss this fair coin and look at it, there is a 50% chance we will see Heads again, and a 50% chance of seeing Tails instead. We can plot this on a probability tree like so:![](images/whatis/whatis1.png)We draw the outcomes on the end of each branch, and the probabilities of each occurrence on the branches. Similarly, if we started in the state Tails and tossed the coin, we would have a 50% chance of seeing Heads and a 50% chance of seeing tails.![](images/whatis/whatis2.png)We can test this works by trying it. You can physically get a coin out, flip it many times, and record each result; you will eventually see roughly 50% of your results are Heads and 50% tails. Around 500 to 1000 tosses should be enough to get reliable results. Experiment 1: Single Coin TossToo lazy to try this? Don’t worry! You can simulate the coin-tossing experiment by pressing the `Toss Coin` button below to simulate a coin toss and store the results. You can change the initial state to 'Heads' or 'Tails', or increase the number of coins (`No. of Coins`) slider to get many results quickly. Click `Reset` to discard your results and start again.<jupyter_code>data_labels = ["Heads", "Tails"]
initial_counts = [0]*2
initial_labels = ["0.00%"]*2

source = ColumnDataSource(data=dict(x=data_labels, top=initial_counts, top_percent=initial_labels))

plot = figure(
              plot_height=500, 
              plot_width=500,
              sizing_mode="scale_width",
              tools="reset",
              x_range=data_labels,
              y_range=DataRange1d(start=0, range_padding = 20, range_padding_units='absolute'),
              y_axis_label="Counts")
plot.vbar(top='top',x='x',width=0.5,source=source, color="#BE95FF")
labels = LabelSet(x='x', y='top', text='top_percent', level='glyph',
        x_offset=-20, y_offset=12, source=source, render_mode='canvas')
plot.add_layout(labels)



# Define buttons and sliders
tossbtn = Button(label="Toss Coin")
repeat_slider = Slider(title="No. of Coins", start=1, end=50, step=1, value=0)
radio_label = Div(text="Initial State:")
init_state_radio = RadioButtonGroup(name="Initial State", labels=data_labels, active=0)
resetbtn = Button(label="Reset")
results_label = Div(text="Results:")
resultsHTML = Div(text="", style={'overflow-y': 'auto', 'max-height': '250px'})

# Define callbacks
toss_callback = CustomJS(args=dict(source=source, p=plot, 
                                   s=repeat_slider, 
                                   resultsHTML=resultsHTML, 
                                   results_label=results_label), 
                code="""
        for (var i = 0; i <= s.value; ++i){
            const result = Math.floor(Math.random() * 2);
            source.data.top[result] += 1;
            resultsHTML.text += [' H ', ' T '][result]
        }
        const total_tosses = source.data.top[0] + source.data.top[1];
        results_label.text = "Results (" + total_tosses + " tosses):";
        
        for (var i = 0; i<2; ++i){
            const frac = source.data.top[i] / source.data.top.reduce((a, b) => a + b, 0);
            source.data.top_percent[i] = (frac*100).toFixed(2) + "%";
        }
        if (Math.max(...source.data.top) > 22) {
            p.y_range.range_padding_units = 'percent';
            p.y_range.range_padding = 1;
        } else {
            p.y_range.range_padding_units = 'absolute';
            p.y_range.range_padding = 30 - Math.max(...source.data.top);
        };
        source.change.emit();
    """)

slider_callback = CustomJS(args=dict(tossbtn=tossbtn), 
                  code="""
    const repeats = cb_obj.value;
    if (repeats == 1) {
        tossbtn.label = "Toss Coin";
    }
    else {
        tossbtn.label = "Toss " + repeats + " Coins";
    };
    """)

reset_callback = CustomJS(args=dict(source=source, resultsHTML=resultsHTML, results_label=results_label), code="""
    source.data.top = [0,0];
    source.data.top_percent = ['0.00%', '0.00%'];
    source.change.emit();
    resultsHTML.text = "";
    results_label.text = "Results:";
    """)

# Link callbacks to buttons / sliders
tossbtn.js_on_event(ButtonClick, toss_callback)
repeat_slider.js_on_change('value', slider_callback)
resetbtn.js_on_event(ButtonClick, reset_callback)

# Compose Layout
control_panel = column(tossbtn, 
                       repeat_slider, 
                       radio_label, 
                       init_state_radio, 
                       resetbtn, 
                       results_label, 
                       resultsHTML)

layout = row(plot, control_panel)

# Output HTML
html_repr = file_html(layout, CDN)
IPython.display.HTML(html_repr)<jupyter_output><empty_output><jupyter_text>Going FurtherIt looks like our probability tree model correctly predicts the results of our experiments. We can go further and chain our probability trees together to predict the outcomes of chains of events. For example, let’s say we start in the state Heads, toss the coin, then _toss the coin again,_ what would we see? We can work it out using the trees:![](images/whatis/whatis3.png)You may remember from school that we multiply along the branches to calculate the probability of each combination of events:![](images/whatis/whatis4.gif)We then add the results together to calculate the probability of each outcome:![](images/whatis/whatis5.gif)And we can see the probability of seeing Heads after these two tosses is 50%, and the probability of seeing Tails after these two tosses is also 50%. Experiment 2: Double Coin TossAs with before, you can simulate the coin-tossing experiment by pressing the `Toss Coin Twice` button to simulate two coin tosses and store the final result. You can change the initial state, or change the `No. of Coins` to get many results quickly. Click `Reset` to discard your results and start again.<jupyter_code>data_labels = ["Heads", "Tails"]
initial_counts = [0]*2
initial_labels = ["0.00%"]*2

source = ColumnDataSource(data=dict(x=data_labels, top=initial_counts, top_percent=initial_labels))

plot = figure(
              plot_height=500, 
              plot_width=500,
              sizing_mode="scale_width",
              tools="reset",
              x_range=data_labels,
              y_range=DataRange1d(start=0, range_padding = 20, range_padding_units='absolute'),
              y_axis_label="Counts")
plot.vbar(top='top',x='x',width=0.5,source=source, color="#BE95FF")
labels = LabelSet(x='x', y='top', text='top_percent', level='glyph',
        x_offset=-20, y_offset=12, source=source, render_mode='canvas')
plot.add_layout(labels)



# Define buttons and sliders
tossbtn = Button(label="Toss Coin Twice")
repeat_slider = Slider(title="No. of Coins", start=1, end=50, step=1, value=0)
radio_label = Div(text="Initial State:")
init_state_radio = RadioButtonGroup(name="Initial State", labels=data_labels, active=0)
resetbtn = Button(label="Reset")
results_label = Div(text="Results:")
resultsHTML = Div(text="", style={'overflow-y': 'auto', 'max-height': '250px'})

# Define callbacks
toss_callback = CustomJS(args=dict(source=source, p=plot, 
                                   s=repeat_slider, 
                                   resultsHTML=resultsHTML, 
                                   results_label=results_label), 
                code="""
        for (var i = 0; i <= s.value; ++i){
            const result = Math.floor(Math.random() * 2);
            source.data.top[result] += 1;
            resultsHTML.text += [' H ', ' T '][result]
        }
        const total_tosses = source.data.top[0] + source.data.top[1];
        results_label.text = "Results (" + total_tosses + " Double Tosses):";
        
        for (var i = 0; i<2; ++i){
            const frac = source.data.top[i] / source.data.top.reduce((a, b) => a + b, 0);
            source.data.top_percent[i] = (frac*100).toFixed(2) + "%";
        }
        if (Math.max(...source.data.top) > 22) {
            p.y_range.range_padding_units = 'percent';
            p.y_range.range_padding = 1;
        } else {
            p.y_range.range_padding_units = 'absolute';
            p.y_range.range_padding = 30 - Math.max(...source.data.top);
        };
        source.change.emit();
    """)

slider_callback = CustomJS(args=dict(tossbtn=tossbtn), 
                  code="""
    const repeats = cb_obj.value;
    if (repeats == 1) {
        tossbtn.label = "Toss Coin Twice";
    }
    else {
        tossbtn.label = "Toss " + repeats + " Coins Twice";
    };
    """)

reset_callback = CustomJS(args=dict(source=source, resultsHTML=resultsHTML, results_label=results_label), code="""
    source.data.top = [0,0];
    source.data.top_percent = ['0.00%', '0.00%'];
    source.change.emit();
    resultsHTML.text = "";
    results_label.text = "Results:";
    """)

# Link callbacks to buttons / sliders
tossbtn.js_on_event(ButtonClick, toss_callback)
repeat_slider.js_on_change('value', slider_callback)
resetbtn.js_on_event(ButtonClick, reset_callback)

# Compose Layout
control_panel = column(tossbtn, 
                       repeat_slider, 
                       radio_label, 
                       init_state_radio, 
                       resetbtn, 
                       results_label, 
                       resultsHTML)

layout = row(plot, control_panel)

# Output HTML
html_repr = file_html(layout, CDN)
IPython.display.HTML(html_repr)<jupyter_output><empty_output><jupyter_text>With enough tosses, our results are as expected: Equal chance of measuring Heads or Tails. The Quantum CoinNow we have a complete description of the classical coin, it’s time to introduce a quantum ‘coin’. Our quantum coin is called a ‘qubit’. A qubit is something you can only play with in a lab, as they are very difficult to manipulate.  Many years of scientific and technological advancements have gone into creating the qubits we have today, but the beauty of learning through quantum computing is that we can ignore the physical complications and just remember that when we measure a qubit, it will be in one of two states: Instead of the two states Heads and Tails, we call our qubit’s two states 0 and 1. Experiment 3: The Quantum Coin TossLet’s experiment with our quantum coin and see how it behaves. We’re going to do a quantum toss, measure the state of our coin, and record it. This is just like the classical coin toss in the section above.<jupyter_code>data_labels = ["0", "1"]
initial_counts = [0]*2
initial_labels = ["0.00%"]*2

source = ColumnDataSource(data=dict(x=data_labels, top=initial_counts, top_percent=initial_labels))

plot = figure(
              plot_height=500, 
              plot_width=500,
              sizing_mode="scale_width",
              tools="reset",
              x_range=data_labels,
              y_range=DataRange1d(start=0, range_padding = 20, range_padding_units='absolute'),
              y_axis_label="Counts")
plot.vbar(top='top',x='x',width=0.5,source=source, color="#BE95FF")
labels = LabelSet(x='x', y='top', text='top_percent', level='glyph',
        x_offset=-20, y_offset=12, source=source, render_mode='canvas')
plot.add_layout(labels)



# Define buttons and sliders
tossbtn = Button(label="Toss Quantum Coin")
repeat_slider = Slider(title="No. of Coins", start=1, end=50, step=1, value=0)
radio_label = Div(text="Initial State:")
init_state_radio = RadioButtonGroup(name="Initial State", labels=data_labels, active=0)
resetbtn = Button(label="Reset")
results_label = Div(text="Results:")
resultsHTML = Div(text="", style={'overflow-y': 'auto', 'max-height': '250px'})

# Define callbacks
toss_callback = CustomJS(args=dict(source=source, p=plot, 
                                   s=repeat_slider, 
                                   resultsHTML=resultsHTML, 
                                   results_label=results_label), 
                code="""
        for (var i = 0; i <= s.value; ++i){
            const result = Math.floor(Math.random() * 2);
            source.data.top[result] += 1;
            resultsHTML.text += [' 0 ', ' 1 '][result]
        }
        const total_tosses = source.data.top[0] + source.data.top[1];
        results_label.text = "Results (" + total_tosses + " Quantum Tosses):";
        
        for (var i = 0; i<2; ++i){
            const frac = source.data.top[i] / source.data.top.reduce((a, b) => a + b, 0);
            source.data.top_percent[i] = (frac*100).toFixed(2) + "%";
        }
        if (Math.max(...source.data.top) > 22) {
            p.y_range.range_padding_units = 'percent';
            p.y_range.range_padding = 1;
        } else {
            p.y_range.range_padding_units = 'absolute';
            p.y_range.range_padding = 30 - Math.max(...source.data.top);
        };
        source.change.emit();
    """)

slider_callback = CustomJS(args=dict(tossbtn=tossbtn), 
                  code="""
    const repeats = cb_obj.value;
    if (repeats == 1) {
        tossbtn.label = "Toss Quantum Coin";
    }
    else {
        tossbtn.label = "Toss " + repeats + " Quantum Coins";
    };
    """)

reset_callback = CustomJS(args=dict(source=source, resultsHTML=resultsHTML, results_label=results_label), code="""
    source.data.top = [0,0];
    source.data.top_percent = ['0.00%', '0.00%'];
    source.change.emit();
    resultsHTML.text = "";
    results_label.text = "Results:";
    """)

# Link callbacks to buttons / sliders
tossbtn.js_on_event(ButtonClick, toss_callback)
repeat_slider.js_on_change('value', slider_callback)
resetbtn.js_on_event(ButtonClick, reset_callback)

# Compose Layout
control_panel = column(tossbtn, 
                       repeat_slider, 
                       radio_label, 
                       init_state_radio, 
                       resetbtn, 
                       results_label, 
                       resultsHTML)

layout = row(plot, control_panel)

# Output HTML
html_repr = file_html(layout, CDN)
IPython.display.HTML(html_repr)<jupyter_output><empty_output><jupyter_text>We’re going to try and describe our quantum coin using probability trees. This looks like, from a 0 state, the coin toss gives us a 50-50 chance of measuring 0 or 1. Let’s plot this on a tree as we did with the classical coin:![](images/whatis/whatis6.png)<jupyter_code>data_labels = ["0", "1"]
initial_counts = [0]*2
initial_labels = ["0.00%"]*2

source = ColumnDataSource(data=dict(x=data_labels, top=initial_counts, top_percent=initial_labels))

plot = figure(
              plot_height=500, 
              plot_width=500,
              sizing_mode="scale_width",
              tools="reset",
              x_range=data_labels,
              y_range=DataRange1d(start=0, range_padding = 20, range_padding_units='absolute'),
              y_axis_label="Counts")
plot.vbar(top='top',x='x',width=0.5,source=source, color="#BE95FF")
labels = LabelSet(x='x', y='top', text='top_percent', level='glyph',
        x_offset=-20, y_offset=12, source=source, render_mode='canvas')
plot.add_layout(labels)



# Define buttons and sliders
tossbtn = Button(label="Toss Quantum Coin")
repeat_slider = Slider(title="No. of Coins", start=1, end=50, step=1, value=0)
radio_label = Div(text="Initial State:")
init_state_radio = RadioButtonGroup(name="Initial State", labels=data_labels, active=1)
resetbtn = Button(label="Reset")
results_label = Div(text="Results:")
resultsHTML = Div(text="", style={'overflow-y': 'auto', 'max-height': '250px'})

# Define callbacks
toss_callback = CustomJS(args=dict(source=source, p=plot, 
                                   s=repeat_slider, 
                                   resultsHTML=resultsHTML, 
                                   results_label=results_label), 
                code="""
        for (var i = 0; i <= s.value; ++i){
            const result = Math.floor(Math.random() * 2);
            source.data.top[result] += 1;
            resultsHTML.text += [' 0 ', ' 1 '][result]
        }
        const total_tosses = source.data.top[0] + source.data.top[1];
        results_label.text = "Results (" + total_tosses + " Quantum Tosses):";
        
        for (var i = 0; i<2; ++i){
            const frac = source.data.top[i] / source.data.top.reduce((a, b) => a + b, 0);
            source.data.top_percent[i] = (frac*100).toFixed(2) + "%";
        }
        if (Math.max(...source.data.top) > 22) {
            p.y_range.range_padding_units = 'percent';
            p.y_range.range_padding = 1;
        } else {
            p.y_range.range_padding_units = 'absolute';
            p.y_range.range_padding = 30 - Math.max(...source.data.top);
        };
        source.change.emit();
    """)

slider_callback = CustomJS(args=dict(tossbtn=tossbtn), 
                  code="""
    const repeats = cb_obj.value;
    if (repeats == 1) {
        tossbtn.label = "Toss Quantum Coin";
    }
    else {
        tossbtn.label = "Toss " + repeats + " Quantum Coins";
    };
    """)

reset_callback = CustomJS(args=dict(source=source, resultsHTML=resultsHTML, results_label=results_label), code="""
    source.data.top = [0,0];
    source.data.top_percent = ['0.00%', '0.00%'];
    source.change.emit();
    resultsHTML.text = "";
    results_label.text = "Results:";
    """)

# Link callbacks to buttons / sliders
tossbtn.js_on_event(ButtonClick, toss_callback)
repeat_slider.js_on_change('value', slider_callback)
resetbtn.js_on_event(ButtonClick, reset_callback)

# Compose Layout
control_panel = column(tossbtn, 
                       repeat_slider, 
                       radio_label, 
                       init_state_radio, 
                       resetbtn, 
                       results_label, 
                       resultsHTML)

layout = row(plot, control_panel)

# Output HTML
html_repr = file_html(layout, CDN)
IPython.display.HTML(html_repr)<jupyter_output><empty_output><jupyter_text>And similarly, it looks like from a 1 state, the coin toss gives us a 50-50 chance of measuring 0 or 1. The probability tree looks like this:![](images/whatis/whatis7.png) Experiment 4: The Double Quantum Coin TossWe now have a model that predicts the behaviour of the quantum coin. Like good scientists, we now want to test it on new scenarios and see if it holds up. Let’s try the double coin toss as we did before. Just like the classical coins, our model of the quantum coin predicts a 50-50 chance of measuring 0 or 1, regardless of which state we start in:![](images/whatis/whatis8.png) So let’s try it! We’re going to toss the quantum coin twice:<jupyter_code>data_labels = ["0", "1"]
initial_counts = [0]*2
initial_labels = ["0.00%"]*2

source = ColumnDataSource(data=dict(x=data_labels, top=initial_counts, top_percent=initial_labels))

plot = figure(
              plot_height=500, 
              plot_width=500,
              sizing_mode="scale_width",
              tools="reset",
              x_range=data_labels,
              y_range=DataRange1d(start=0, range_padding = 20, range_padding_units='absolute'),
              y_axis_label="Counts")
plot.vbar(top='top',x='x',width=0.5,source=source, color="#BE95FF")
labels = LabelSet(x='x', y='top', text='top_percent', level='glyph',
        x_offset=-20, y_offset=12, source=source, render_mode='canvas')
plot.add_layout(labels)



# Define buttons and sliders
tossbtn = Button(label="Toss Quantum Coin Twice")
repeat_slider = Slider(title="No. of Coins", start=1, end=50, step=1, value=0)
radio_label = Div(text="Initial State:")
init_state_radio = RadioButtonGroup(name="Initial State", labels=data_labels, active=0)
resetbtn = Button(label="Reset")
results_label = Div(text="Results:")
resultsHTML = Div(text="", style={'overflow-y': 'auto', 'max-height': '250px'})

# Define callbacks
toss_callback = CustomJS(args=dict(source=source, p=plot, 
                                   s=repeat_slider, 
                                   resultsHTML=resultsHTML, 
                                   results_label=results_label,
                                init_state_radio=init_state_radio), 
                code="""
        for (var i = 0; i <= s.value; ++i){
            const result = init_state_radio.active;
            source.data.top[result] += 1;
            resultsHTML.text += [' 0 ', ' 1 '][result]
        }
        const total_tosses = source.data.top[0] + source.data.top[1];
        results_label.text = "Results (" + total_tosses + " Double Quantum Tosses):";
        
        for (var i = 0; i<2; ++i){
            const frac = source.data.top[i] / source.data.top.reduce((a, b) => a + b, 0);
            source.data.top_percent[i] = (frac*100).toFixed(2) + "%";
        }
        if (Math.max(...source.data.top) > 22) {
            p.y_range.range_padding_units = 'percent';
            p.y_range.range_padding = 1;
        } else {
            p.y_range.range_padding_units = 'absolute';
            p.y_range.range_padding = 30 - Math.max(...source.data.top);
        };
        source.change.emit();
    """)

slider_callback = CustomJS(args=dict(tossbtn=tossbtn), 
                  code="""
    const repeats = cb_obj.value;
    if (repeats == 1) {
        tossbtn.label = "Toss Quantum Coin Twice";
    }
    else {
        tossbtn.label = "Toss " + repeats + " Quantum Coins Twice";
    };
    """)

reset_callback = CustomJS(args=dict(source=source, resultsHTML=resultsHTML, results_label=results_label), code="""
    source.data.top = [0,0];
    source.data.top_percent = ['0.00%', '0.00%'];
    source.change.emit();
    resultsHTML.text = "";
    results_label.text = "Results:";
    """)

# Link callbacks to buttons / sliders
tossbtn.js_on_event(ButtonClick, toss_callback)
repeat_slider.js_on_change('value', slider_callback)
resetbtn.js_on_event(ButtonClick, reset_callback)

# Compose Layout
control_panel = column(tossbtn, 
                       repeat_slider, 
                       radio_label, 
                       init_state_radio, 
                       resetbtn, 
                       results_label, 
                       resultsHTML)

layout = row(plot, control_panel)

# Output HTML
html_repr = file_html(layout, CDN)
IPython.display.HTML(html_repr)<jupyter_output><empty_output><jupyter_text>Hmm… this is an unexpected result. Let's see what happens when we set the initial state to 1:<jupyter_code>data_labels = ["0", "1"]
initial_counts = [0]*2
initial_labels = ["0.00%"]*2

source = ColumnDataSource(data=dict(x=data_labels, top=initial_counts, top_percent=initial_labels))

plot = figure(
              plot_height=500, 
              plot_width=500,
              sizing_mode="scale_width",
              tools="reset",
              x_range=data_labels,
              y_range=DataRange1d(start=0, range_padding = 20, range_padding_units='absolute'),
              y_axis_label="Counts")
plot.vbar(top='top',x='x',width=0.5,source=source, color="#BE95FF")
labels = LabelSet(x='x', y='top', text='top_percent', level='glyph',
        x_offset=-20, y_offset=12, source=source, render_mode='canvas')
plot.add_layout(labels)



# Define buttons and sliders
tossbtn = Button(label="Toss Quantum Coin Twice")
repeat_slider = Slider(title="No. of Coins", start=1, end=50, step=1, value=0)
radio_label = Div(text="Initial State:")
init_state_radio = RadioButtonGroup(name="Initial State", labels=data_labels, active=1)
resetbtn = Button(label="Reset")
results_label = Div(text="Results:")
resultsHTML = Div(text="", style={'overflow-y': 'auto', 'max-height': '250px'})

# Define callbacks
toss_callback = CustomJS(args=dict(source=source, p=plot, 
                                   s=repeat_slider, 
                                   resultsHTML=resultsHTML, 
                                   results_label=results_label,
                                init_state_radio=init_state_radio), 
                code="""
        for (var i = 0; i <= s.value; ++i){
            const result = init_state_radio.active;
            source.data.top[result] += 1;
            resultsHTML.text += [' 0 ', ' 1 '][result]
        }
        const total_tosses = source.data.top[0] + source.data.top[1];
        results_label.text = "Results (" + total_tosses + " Quantum Tosses)";
        
        for (var i = 0; i<2; ++i){
            const frac = source.data.top[i] / source.data.top.reduce((a, b) => a + b, 0);
            source.data.top_percent[i] = (frac*100).toFixed(2) + "%";
        }
        if (Math.max(...source.data.top) > 22) {
            p.y_range.range_padding_units = 'percent';
            p.y_range.range_padding = 1;
        } else {
            p.y_range.range_padding_units = 'absolute';
            p.y_range.range_padding = 30 - Math.max(...source.data.top);
        };
        source.change.emit();
    """)

slider_callback = CustomJS(args=dict(tossbtn=tossbtn), 
                  code="""
    const repeats = cb_obj.value;
    if (repeats == 1) {
        tossbtn.label = "Toss Quantum Coin Twice";
    }
    else {
        tossbtn.label = "Toss " + repeats + " Quantum Coins Twice";
    };
    """)

reset_callback = CustomJS(args=dict(source=source, resultsHTML=resultsHTML, results_label=results_label), code="""
    source.data.top = [0,0];
    source.data.top_percent = ['0.00%', '0.00%'];
    source.change.emit();
    resultsHTML.text = "";
    results_label.text = "Results:";
    """)

# Link callbacks to buttons / sliders
tossbtn.js_on_event(ButtonClick, toss_callback)
repeat_slider.js_on_change('value', slider_callback)
resetbtn.js_on_event(ButtonClick, reset_callback)

# Compose Layout
control_panel = column(tossbtn, 
                       repeat_slider, 
                       radio_label, 
                       init_state_radio, 
                       resetbtn, 
                       results_label, 
                       resultsHTML)

layout = row(plot, control_panel)

# Output HTML
html_repr = file_html(layout, CDN)
IPython.display.HTML(html_repr)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12036572248117605, 'fraction_numerical': 0.015310147006095374, 'mean_word_length': 2.822779605263158, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '16674', 'license': 'Apache-2.0', 'n_tokens_mistral': 8856, 'n_tokens_neox': 8342, 'n_words': 2200, 'path': 'content/what-is-quantum.ipynb', 'repo_name': 'duartefrazao/qiskit-textbook', 'size': '27890'}