diff --git a/pct/_config.yml b/pct/_config.yml
index aa5bceb..c12eda6 100644
--- a/pct/_config.yml
+++ b/pct/_config.yml
@@ -7,7 +7,7 @@
# Book settings
title : PowerCyber Training # The title of the book. Will be placed in the left navbar.
author : Hantao Cui & Xin Fang # The author of the book
-copyright : "2024" # Copyright year to be placed in the footer
+copyright : "2024-2025" # Copyright year to be placed in the footer
logo : logo.png # A path to the book logo
# Force re-execution of notebooks on each build.
@@ -32,7 +32,7 @@ bibtex_bibfiles:
# Information about where the book exists on the web
repository:
- url: https://github.com/cuihantao/PowerCyber Training # Online location of your book
+ url: https://github.com/PowerCyberTraining/powercybertraining.github.io # Online location of your book
path_to_book: docs # Optional path to your book, relative to the repository root
branch: main # Which branch of the repository should be used when creating links (optional)
@@ -41,4 +41,5 @@ repository:
html:
use_issues_button: true
use_repository_button: true
-
+ comments:
+ hypothesis: true
diff --git a/pct/_toc.yml b/pct/_toc.yml
index 6067289..da13b39 100644
--- a/pct/_toc.yml
+++ b/pct/_toc.yml
@@ -5,7 +5,6 @@ format: jb-book
root: intro
chapters:
- file: how-to-use.md
- title: How to use this book
- file: modules/01/intro.md
sections:
- file: modules/01/wsl.ipynb
diff --git a/pct/content.md b/pct/content.md
deleted file mode 100644
index 0f6aca7..0000000
--- a/pct/content.md
+++ /dev/null
@@ -1,5 +0,0 @@
-Content in Jupyter Book
-=======================
-
-There are many ways to write content in Jupyter Book. This short section
-covers a few tips for how to do so.
diff --git a/pct/how-to-use.md b/pct/how-to-use.md
index fe71855..d4fa7e9 100644
--- a/pct/how-to-use.md
+++ b/pct/how-to-use.md
@@ -1,8 +1,37 @@
-
-# How to use this book
+# How to Follow the Training
This book is designed to be *interactive* and *hands-on*. To get the most out of your learning experience, we recommend the following approach:
+## Navigating This Website
+
+This website is designed for easy navigation:
+
+- The **left sidebar** contains the table of contents with collapsible sections for each topic area.
+- The **right side** displays the current page content.
+- Use the **Next** and **Previous** links at the bottom of each page to move sequentially through lessons.
+- In the top-right corner, you'll find buttons for **full screen mode**,
+ **dark/light mode**, and other display options to customize your reading
+ experience.
+
+## Highlighting and Annotating using Hypothesis
+
+This website supports **Hypothesis**, an annotation overlay that allows you to
+highlight text and add notes. Simply select any text to highlight it and add
+your questions or thoughts. This feature is great for collaborative learning
+and asking questions about specific content.
+
+```{admonition} Note
+:class: tip
+
+You will need to create a free account with Hypothesis to use this feature.
+
+```
+
+You can open the Hypothesis panel
+by clicking the "<" button in the top-right corner of the page. The other two
+buttons below allows you to switch on and off the highlights and write notes for
+the page.
+
## Setting up your environment
1. **Begin with Module 1**: Start by following the environment setup instructions in Module 1, which will guide you through installing necessary tools (WSL for Windows users, command-line basics, package managers, and development environments).
diff --git a/pct/intro.md b/pct/intro.md
index d228970..ed4ece9 100644
--- a/pct/intro.md
+++ b/pct/intro.md
@@ -1,19 +1,38 @@
# PowerCyber Training
-Welcome to the PowerCyber Training website!
+Welcome to the PowerCyber Training website! We are glad to see you here!
-This initiative aims to advance power engineering by providing extensive training in state-of-the-art cyberinfrastructure (CI) technologies. It seeks to empower researchers and professionals with the requisite expertise for innovation in this dynamic field. The workshop is organized around three core objectives to foster a comprehensive grasp of theoretical and practical aspects of power engineering and CI.
+This website hosts training materials for power engineering researchers to learn
+about modern cyberinfrastructure (CI) technologies. The goal is to equip
+researchers with the skills to apply CI technologies for research advancement.
+These lessons are designed to be hands-on and self-paced,
+meaning that you can work through them by running the code examples and
+following the explanations.
+This is an ongoing project, and materials will be added and updated regularly.
+Lessons are being updated to include an information table at the beginning. The
+table will indicate the status of the lesson (e.g., "In Progress", "Completed").
+We thank you for your patience as we work to complete the materials.
+
+To start, please click on the "Next" link at the bottom of this page. You can
+also use the arrow key on your keyboard to navigate through the website.
-The contents of this book are licensed for free consumption under the following
-license: Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
-International (CC BY-NC-ND 4.0).
-# Acknowledgements
+```{admonition} Acknowledgements
This work is supported by the National Science Foundation (NSF) under [Grant No.
2319895 and 2319896](https://www.nsf.gov/awardsearch/showAward?AWD_ID=2319895). Any
opinions, findings, and conclusions or recommendations expressed in this
material are those of the author(s) and do not necessarily reflect the views of
the National Science Foundation.
+```
+
+```{admonition} License
+
+The contents of this book are licensed for free consumption under the following
+license: Creative Commons Attribution 4.0 International (CC-BY-4.0). We welcome
+you to share and adapt the contents for your own purposes, provided that you
+properly credit the original authors.
+
+```
diff --git a/pct/markdown-notebooks.md b/pct/markdown-notebooks.md
deleted file mode 100644
index 6d97104..0000000
--- a/pct/markdown-notebooks.md
+++ /dev/null
@@ -1,54 +0,0 @@
----
-jupytext:
- cell_metadata_filter: -all
- formats: md:myst
- text_representation:
- extension: .md
- format_name: myst
- format_version: 0.13
- jupytext_version: 1.11.5
-kernelspec:
- display_name: Python 3
- language: python
- name: python3
----
-
-# Notebooks with MyST Markdown
-
-Jupyter Book also lets you write text-based notebooks using MyST Markdown.
-See [the Notebooks with MyST Markdown documentation](https://jupyterbook.org/file-types/myst-notebooks.html) for more detailed instructions.
-This page shows off a notebook written in MyST Markdown.
-
-## An example cell
-
-With MyST Markdown, you can define code cells with a directive like so:
-
-```{code-cell}
-print(2 + 2)
-```
-
-When your book is built, the contents of any `{code-cell}` blocks will be
-executed with your default Jupyter kernel, and their outputs will be displayed
-in-line with the rest of your content.
-
-```{seealso}
-Jupyter Book uses [Jupytext](https://jupytext.readthedocs.io/en/latest/) to convert text-based files to notebooks, and can support [many other text-based notebook files](https://jupyterbook.org/file-types/jupytext.html).
-```
-
-## Create a notebook with MyST Markdown
-
-MyST Markdown notebooks are defined by two things:
-
-1. YAML metadata that is needed to understand if / how it should convert text files to notebooks (including information about the kernel needed).
- See the YAML at the top of this page for example.
-2. The presence of `{code-cell}` directives, which will be executed with your book.
-
-That's all that is needed to get started!
-
-## Quickly add YAML metadata for MyST Notebooks
-
-If you have a markdown file and you'd like to quickly add YAML metadata to it, so that Jupyter Book will treat it as a MyST Markdown Notebook, run the following command:
-
-```
-jupyter-book myst init path/to/markdownfile.md
-```
\ No newline at end of file
diff --git a/pct/markdown.md b/pct/markdown.md
deleted file mode 100644
index 55dd2e6..0000000
--- a/pct/markdown.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# Markdown Files
-
-Whether you write your book's content in Jupyter Notebooks (`.ipynb`) or
-in regular markdown files (`.md`), you'll write in the same flavor of markdown
-called **MyST Markdown**.
-This is a simple file to help you get started and show off some syntax.
-
-## What is MyST?
-
-MyST stands for "Markedly Structured Text". It
-is a slight variation on a flavor of markdown called "CommonMark" markdown,
-with small syntax extensions to allow you to write **roles** and **directives**
-in the Sphinx ecosystem.
-
-For more about MyST, see [the MyST Markdown Overview](https://jupyterbook.org/content/myst.html).
-
-## Sample Roles and Directives
-
-Roles and directives are two of the most powerful tools in Jupyter Book. They
-are kind of like functions, but written in a markup language. They both
-serve a similar purpose, but **roles are written in one line**, whereas
-**directives span many lines**. They both accept different kinds of inputs,
-and what they do with those inputs depends on the specific role or directive
-that is being called.
-
-Here is a "note" directive:
-
-```{note}
-Here is a note
-```
-
-It will be rendered in a special box when you build your book.
-
-Here is an inline directive to refer to a document: {doc}`markdown-notebooks`.
-
-
-## Citations
-
-You can also cite references that are stored in a `bibtex` file. For example,
-the following syntax: `` {cite}`holdgraf_evidence_2014` `` will render like
-this: {cite}`holdgraf_evidence_2014`.
-
-Moreover, you can insert a bibliography into your page with this syntax:
-The `{bibliography}` directive must be used for all the `{cite}` roles to
-render properly.
-For example, if the references for your book are stored in `references.bib`,
-then the bibliography is inserted with:
-
-```{bibliography}
-:filter: docname in docnames
-```
-
-## Learn more
-
-This is just a simple starter to get you started.
-You can learn a lot more at [jupyterbook.org](https://jupyterbook.org).
\ No newline at end of file
diff --git a/pct/modules/01/intro.md b/pct/modules/01/intro.md
index b180830..44a88cb 100644
--- a/pct/modules/01/intro.md
+++ b/pct/modules/01/intro.md
@@ -1,4 +1,4 @@
-# Tools You Need
+# Computational Environment Fundamentals
## Motivation
@@ -9,7 +9,7 @@ researchers to figure out how to install, configure, and efficiently use these
tools for research.
Most power engineering researchers are familiar with the Microsoft Windows
-operating system and the MATLAB software. However, many of the advanced
+operating system and probably also with MATLAB. However, many of the advanced
cyberinfrastructure tools are developed on Linux and thus compile more easily
and run more efficiently in Linux. On the other hand, compiling and configuring
some tools to properly run on Windows may take significantly more efforts.
@@ -19,8 +19,8 @@ important for research productivity.
This module aims to provide an introduction to several tools on Linux. These
include
- Windows Subsystem for Linux (WSL)
-- Shell
-- Anaconda/Mambaforge
-- VS Code
+- Linux Command Line Interface (CLI)
+- Python Package Management
+- Visual Studio Code IDE
- Jupyter Notebook
- git
\ No newline at end of file
diff --git a/pct/modules/04/linear-equations.ipynb b/pct/modules/04/linear-equations.ipynb
index 5300619..5aea55b 100644
--- a/pct/modules/04/linear-equations.ipynb
+++ b/pct/modules/04/linear-equations.ipynb
@@ -778,7 +778,7 @@
"tags": []
},
"source": [
- ":::{admonition}\n",
+ ":::{admonition} NumPy version compatibility\n",
":class: warning\n",
"\n",
"NumPy 1.24 is required for `scikits.umfpack`. The latest 1.26 (as of 4/15/2025) is not compatible. The error message is\n",
@@ -1241,1093 +1241,7 @@
},
{
"data": {
- "application/javascript": [
- "var questionsKIRfHxXXCJUU=[\n",
- " {\n",
- " \"question\": \"What is the standard form for a system of linear equations?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"Ax = b\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! A system of linear equations is written in standard form as Ax = b, where A is the coefficient matrix, x is the vector of unknowns, and b is the right-hand side vector.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"x = Ab\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. The standard form is Ax = b, not x = Ab.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"x = A^(-1)b\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. This is the solution to a system of linear equations (x = A^(-1)b), not the standard form of the system itself.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"b = xA\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. The standard form of a system of linear equations is Ax = b, not b = xA.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"When is the solution to a system of linear equations Ax = b unique?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"When A is square and nonsingular (det(A) ≠ 0)\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! A unique solution exists when A is a square matrix and has a non-zero determinant, meaning its rows or columns are linearly independent.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"When A is any rectangular matrix\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. Rectangular matrices (more equations than unknowns or vice versa) typically lead to either no solution or infinitely many solutions.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"When A is a singular matrix\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. A singular matrix (det(A) = 0) means the system either has no solution or infinitely many solutions.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"When A is a diagonal matrix\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. While diagonal matrices with non-zero diagonal elements do have unique solutions, this is not a necessary condition. Any nonsingular matrix will have a unique solution.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"Which of the following is the recommended way to solve a system of linear equations in Python?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"np.linalg.solve(A, b)\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! Using np.linalg.solve is the recommended approach as it's more numerically stable and computationally efficient than computing the inverse.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"np.linalg.inv(A) @ b\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. While this will work mathematically, it's not recommended because matrix inversion is numerically unstable and computationally expensive.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"A * b\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. This would perform element-wise multiplication, not solve the system of equations.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"A @ b\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. This performs matrix multiplication, not solving for x in Ax = b.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"What is the main advantage of LU decomposition for solving linear systems?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"It allows efficient solving for multiple right-hand sides after a single decomposition\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! Once the LU decomposition is computed, it can be reused to solve for different right-hand side vectors without repeating the most expensive part of the computation.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It always guarantees a solution even for singular matrices\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. LU decomposition does not guarantee solutions for singular matrices.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It's the only method that works with sparse matrices\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. There are several methods that work with sparse matrices, including iterative methods.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It requires less memory than other solution methods\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. While memory efficiency isn't the main advantage, the key benefit is computational efficiency when solving multiple systems with the same coefficient matrix.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"What makes a matrix 'sparse'?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"It contains many zero elements\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! A sparse matrix has many zero elements, allowing for specialized storage formats and algorithms that improve efficiency.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It has more columns than rows\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. A matrix with more columns than rows is called a wide matrix or has a rectangular shape, but this doesn't make it sparse.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"Its determinant is close to zero\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. A matrix with a determinant close to zero is called ill-conditioned, not sparse.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It contains only integer values\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. The type of values in a matrix doesn't determine whether it's sparse. Sparsity refers to the number of zero elements.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"Which of the following SciPy sparse matrix formats is most efficient for incrementally building a matrix?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"dok_matrix\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! dok_matrix (Dictionary of Keys) is based on a hash table and allows efficient random access, making it ideal for incrementally building a matrix.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"csr_matrix\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. While csr_matrix (Compressed Sparse Row) is efficient for arithmetic operations and row slicing, it's not the best choice for incrementally building a matrix.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"csc_matrix\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. csc_matrix (Compressed Sparse Column) is efficient for arithmetic operations and column slicing, but not for incrementally building a matrix.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"dia_matrix\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. dia_matrix is efficient for storing diagonal matrices but isn't designed for incrementally building general matrices.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"Which function in SciPy should be used to solve sparse linear systems?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"scipy.sparse.linalg.spsolve\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! spsolve is specifically designed to solve sparse linear systems efficiently.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"scipy.linalg.solve\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. scipy.linalg.solve is designed for dense matrices and would not take advantage of the sparsity structure.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"scipy.sparse.solve\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. This is not a valid function in SciPy. The correct function is scipy.sparse.linalg.spsolve.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"scipy.sparse.linalg.eigs\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. eigs computes eigenvalues and eigenvectors, not solutions to linear systems.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"Which sparse solver has shown superior performance for electrical circuit problems?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"KLU\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! As mentioned in the notebook, KLU (Kent Clark LU) shows superior performance for electrical circuit problems, including power systems.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"SuperLU\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. While SuperLU is a good general-purpose sparse solver, KLU is specifically optimized for circuit problems.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"UMFPACK\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. UMFPACK is another good general-purpose sparse solver, but it's not specifically optimized for electrical circuit problems like KLU.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"LAPACK\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. LAPACK is primarily for dense linear algebra, not sparse systems.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"Why is it not recommended to use matrix inversion (A⁻¹) to solve linear systems?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"It's numerically unstable and computationally expensive\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! Matrix inversion is both numerically less stable and computationally more expensive than direct solving methods like LU decomposition.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It only works for square matrices\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. While it's true that only square matrices can be inverted, this isn't the main reason to avoid using inversion for solving systems.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It's not supported in NumPy\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. NumPy does support matrix inversion with np.linalg.inv(), but it's still not recommended for solving systems.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It only works for real-valued matrices\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. Matrix inversion works for complex-valued matrices as well, but is still not the recommended approach.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"What is the primary advantage of using a sparse matrix representation for power system admittance matrices?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"Better efficiency and less memory usage\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! Power system admittance matrices are naturally sparse because buses are only connected to a few other buses. Using sparse representations provides better computational efficiency and significantly reduced memory usage.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"More accurate results in power flow calculations\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. Sparse matrix representation affects efficiency, not accuracy. The mathematical results should be identical to using dense matrices.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"Ability to handle complex numbers\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. Both sparse and dense matrices in Python can handle complex numbers for admittance matrices.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"Simplified matrix construction\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. Sparse matrices can actually be more complex to construct, but the efficiency benefits outweigh this complexity for large power systems.\"\n",
- " }\n",
- " ]\n",
- " }\n",
- "]\n",
- ";\n",
- " // Make a random ID\n",
- "function makeid(length) {\n",
- " var result = [];\n",
- " var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\n",
- " var charactersLength = characters.length;\n",
- " for (var i = 0; i < length; i++) {\n",
- " result.push(characters.charAt(Math.floor(Math.random() * charactersLength)));\n",
- " }\n",
- " return result.join('');\n",
- "}\n",
- "\n",
- "// Choose a random subset of an array. Can also be used to shuffle the array\n",
- "function getRandomSubarray(arr, size) {\n",
- " var shuffled = arr.slice(0), i = arr.length, temp, index;\n",
- " while (i--) {\n",
- " index = Math.floor((i + 1) * Math.random());\n",
- " temp = shuffled[index];\n",
- " shuffled[index] = shuffled[i];\n",
- " shuffled[i] = temp;\n",
- " }\n",
- " return shuffled.slice(0, size);\n",
- "}\n",
- "\n",
- "function printResponses(responsesContainer) {\n",
- " var responses=JSON.parse(responsesContainer.dataset.responses);\n",
- " var stringResponses='IMPORTANT!To preserve this answer sequence for submission, when you have finalized your answers:
- Copy the text in this cell below \"Answer String\"
- Double click on the cell directly below the Answer String, labeled \"Replace Me\"
- Select the whole \"Replace Me\" text
- Paste in your answer string and press shift-Enter.
- Save the notebook using the save icon or File->Save Notebook menu item
Answer String:
';\n",
- " console.log(responses);\n",
- " responses.forEach((response, index) => {\n",
- " if (response) {\n",
- " console.log(index + ': ' + response);\n",
- " stringResponses+= index + ': ' + response +\"
\";\n",
- " }\n",
- " });\n",
- " responsesContainer.innerHTML=stringResponses;\n",
- "}\n",
- "/* Callback function to determine whether a selected multiple-choice\n",
- " button corresponded to a correct answer and to provide feedback\n",
- " based on the answer */\n",
- "function check_mc() {\n",
- " var id = this.id.split('-')[0];\n",
- " //var response = this.id.split('-')[1];\n",
- " //console.log(response);\n",
- " //console.log(\"In check_mc(), id=\"+id);\n",
- " //console.log(event.srcElement.id) \n",
- " //console.log(event.srcElement.dataset.correct) \n",
- " //console.log(event.srcElement.dataset.feedback)\n",
- "\n",
- " var label = event.srcElement;\n",
- " //console.log(label, label.nodeName);\n",
- " var depth = 0;\n",
- " while ((label.nodeName != \"LABEL\") && (depth < 20)) {\n",
- " label = label.parentElement;\n",
- " console.log(depth, label);\n",
- " depth++;\n",
- " }\n",
- "\n",
- "\n",
- "\n",
- " var answers = label.parentElement.children;\n",
- " //console.log(answers);\n",
- "\n",
- " // Split behavior based on multiple choice vs many choice:\n",
- " var fb = document.getElementById(\"fb\" + id);\n",
- "\n",
- "\n",
- "\n",
- " /* Multiple choice (1 answer). Allow for 0 correct\n",
- " answers as an edge case */\n",
- " if (fb.dataset.numcorrect <= 1) {\n",
- " // What follows is for the saved responses stuff\n",
- " var outerContainer = fb.parentElement.parentElement;\n",
- " var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n",
- " if (responsesContainer) {\n",
- " //console.log(responsesContainer);\n",
- " var response = label.firstChild.innerText;\n",
- " if (label.querySelector(\".QuizCode\")){\n",
- " response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n",
- " }\n",
- " console.log(response);\n",
- " //console.log(document.getElementById(\"quizWrap\"+id));\n",
- " var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n",
- " console.log(\"Question \" + qnum);\n",
- " //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n",
- " var responses=JSON.parse(responsesContainer.dataset.responses);\n",
- " console.log(responses);\n",
- " responses[qnum]= response;\n",
- " responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n",
- " printResponses(responsesContainer);\n",
- " }\n",
- " // End code to preserve responses\n",
- "\n",
- " for (var i = 0; i < answers.length; i++) {\n",
- " var child = answers[i];\n",
- " //console.log(child);\n",
- " child.className = \"MCButton\";\n",
- " }\n",
- "\n",
- "\n",
- "\n",
- " if (label.dataset.correct == \"true\") {\n",
- " // console.log(\"Correct action\");\n",
- " if (\"feedback\" in label.dataset) {\n",
- " fb.innerHTML = jaxify(label.dataset.feedback);\n",
- " } else {\n",
- " fb.innerHTML = \"Correct!\";\n",
- " }\n",
- " label.classList.add(\"correctButton\");\n",
- "\n",
- " fb.className = \"Feedback\";\n",
- " fb.classList.add(\"correct\");\n",
- "\n",
- " } else {\n",
- " if (\"feedback\" in label.dataset) {\n",
- " fb.innerHTML = jaxify(label.dataset.feedback);\n",
- " } else {\n",
- " fb.innerHTML = \"Incorrect -- try again.\";\n",
- " }\n",
- " //console.log(\"Error action\");\n",
- " label.classList.add(\"incorrectButton\");\n",
- " fb.className = \"Feedback\";\n",
- " fb.classList.add(\"incorrect\");\n",
- " }\n",
- " }\n",
- " else { /* Many choice (more than 1 correct answer) */\n",
- " var reset = false;\n",
- " var feedback;\n",
- " if (label.dataset.correct == \"true\") {\n",
- " if (\"feedback\" in label.dataset) {\n",
- " feedback = jaxify(label.dataset.feedback);\n",
- " } else {\n",
- " feedback = \"Correct!\";\n",
- " }\n",
- " if (label.dataset.answered <= 0) {\n",
- " if (fb.dataset.answeredcorrect < 0) {\n",
- " fb.dataset.answeredcorrect = 1;\n",
- " reset = true;\n",
- " } else {\n",
- " fb.dataset.answeredcorrect++;\n",
- " }\n",
- " if (reset) {\n",
- " for (var i = 0; i < answers.length; i++) {\n",
- " var child = answers[i];\n",
- " child.className = \"MCButton\";\n",
- " child.dataset.answered = 0;\n",
- " }\n",
- " }\n",
- " label.classList.add(\"correctButton\");\n",
- " label.dataset.answered = 1;\n",
- " fb.className = \"Feedback\";\n",
- " fb.classList.add(\"correct\");\n",
- "\n",
- " }\n",
- " } else {\n",
- " if (\"feedback\" in label.dataset) {\n",
- " feedback = jaxify(label.dataset.feedback);\n",
- " } else {\n",
- " feedback = \"Incorrect -- try again.\";\n",
- " }\n",
- " if (fb.dataset.answeredcorrect > 0) {\n",
- " fb.dataset.answeredcorrect = -1;\n",
- " reset = true;\n",
- " } else {\n",
- " fb.dataset.answeredcorrect--;\n",
- " }\n",
- "\n",
- " if (reset) {\n",
- " for (var i = 0; i < answers.length; i++) {\n",
- " var child = answers[i];\n",
- " child.className = \"MCButton\";\n",
- " child.dataset.answered = 0;\n",
- " }\n",
- " }\n",
- " label.classList.add(\"incorrectButton\");\n",
- " fb.className = \"Feedback\";\n",
- " fb.classList.add(\"incorrect\");\n",
- " }\n",
- " // What follows is for the saved responses stuff\n",
- " var outerContainer = fb.parentElement.parentElement;\n",
- " var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n",
- " if (responsesContainer) {\n",
- " //console.log(responsesContainer);\n",
- " var response = label.firstChild.innerText;\n",
- " if (label.querySelector(\".QuizCode\")){\n",
- " response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n",
- " }\n",
- " console.log(response);\n",
- " //console.log(document.getElementById(\"quizWrap\"+id));\n",
- " var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n",
- " console.log(\"Question \" + qnum);\n",
- " //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n",
- " var responses=JSON.parse(responsesContainer.dataset.responses);\n",
- " if (label.dataset.correct == \"true\") {\n",
- " if (typeof(responses[qnum]) == \"object\"){\n",
- " if (!responses[qnum].includes(response))\n",
- " responses[qnum].push(response);\n",
- " } else{\n",
- " responses[qnum]= [ response ];\n",
- " }\n",
- " } else {\n",
- " responses[qnum]= response;\n",
- " }\n",
- " console.log(responses);\n",
- " responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n",
- " printResponses(responsesContainer);\n",
- " }\n",
- " // End save responses stuff\n",
- "\n",
- "\n",
- "\n",
- " var numcorrect = fb.dataset.numcorrect;\n",
- " var answeredcorrect = fb.dataset.answeredcorrect;\n",
- " if (answeredcorrect >= 0) {\n",
- " fb.innerHTML = feedback + \" [\" + answeredcorrect + \"/\" + numcorrect + \"]\";\n",
- " } else {\n",
- " fb.innerHTML = feedback + \" [\" + 0 + \"/\" + numcorrect + \"]\";\n",
- " }\n",
- "\n",
- "\n",
- " }\n",
- "\n",
- " if (typeof MathJax != 'undefined') {\n",
- " var version = MathJax.version;\n",
- " console.log('MathJax version', version);\n",
- " if (version[0] == \"2\") {\n",
- " MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n",
- " } else if (version[0] == \"3\") {\n",
- " MathJax.typeset([fb]);\n",
- " }\n",
- " } else {\n",
- " console.log('MathJax not detected');\n",
- " }\n",
- "\n",
- "}\n",
- "\n",
- "\n",
- "/* Function to produce the HTML buttons for a multiple choice/\n",
- " many choice question and to update the CSS tags based on\n",
- " the question type */\n",
- "function make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id) {\n",
- "\n",
- " var shuffled;\n",
- " if (shuffle_answers == \"True\") {\n",
- " //console.log(shuffle_answers+\" read as true\");\n",
- " shuffled = getRandomSubarray(qa.answers, qa.answers.length);\n",
- " } else {\n",
- " //console.log(shuffle_answers+\" read as false\");\n",
- " shuffled = qa.answers;\n",
- " }\n",
- "\n",
- "\n",
- " var num_correct = 0;\n",
- "\n",
- " shuffled.forEach((item, index, ans_array) => {\n",
- " //console.log(answer);\n",
- "\n",
- " // Make input element\n",
- " var inp = document.createElement(\"input\");\n",
- " inp.type = \"radio\";\n",
- " inp.id = \"quizo\" + id + index;\n",
- " inp.style = \"display:none;\";\n",
- " aDiv.append(inp);\n",
- "\n",
- " //Make label for input element\n",
- " var lab = document.createElement(\"label\");\n",
- " lab.className = \"MCButton\";\n",
- " lab.id = id + '-' + index;\n",
- " lab.onclick = check_mc;\n",
- " var aSpan = document.createElement('span');\n",
- " aSpan.classsName = \"\";\n",
- " //qDiv.id=\"quizQn\"+id+index;\n",
- " if (\"answer\" in item) {\n",
- " aSpan.innerHTML = jaxify(item.answer);\n",
- " //aSpan.innerHTML=item.answer;\n",
- " }\n",
- " lab.append(aSpan);\n",
- "\n",
- " // Create div for code inside question\n",
- " var codeSpan;\n",
- " if (\"code\" in item) {\n",
- " codeSpan = document.createElement('span');\n",
- " codeSpan.id = \"code\" + id + index;\n",
- " codeSpan.className = \"QuizCode\";\n",
- " var codePre = document.createElement('pre');\n",
- " codeSpan.append(codePre);\n",
- " var codeCode = document.createElement('code');\n",
- " codePre.append(codeCode);\n",
- " codeCode.innerHTML = item.code;\n",
- " lab.append(codeSpan);\n",
- " //console.log(codeSpan);\n",
- " }\n",
- "\n",
- " //lab.textContent=item.answer;\n",
- "\n",
- " // Set the data attributes for the answer\n",
- " lab.setAttribute('data-correct', item.correct);\n",
- " if (item.correct) {\n",
- " num_correct++;\n",
- " }\n",
- " if (\"feedback\" in item) {\n",
- " lab.setAttribute('data-feedback', item.feedback);\n",
- " }\n",
- " lab.setAttribute('data-answered', 0);\n",
- "\n",
- " aDiv.append(lab);\n",
- "\n",
- " });\n",
- "\n",
- " if (num_correct > 1) {\n",
- " outerqDiv.className = \"ManyChoiceQn\";\n",
- " } else {\n",
- " outerqDiv.className = \"MultipleChoiceQn\";\n",
- " }\n",
- "\n",
- " return num_correct;\n",
- "\n",
- "}\n",
- "function check_numeric(ths, event) {\n",
- "\n",
- " if (event.keyCode === 13) {\n",
- " ths.blur();\n",
- "\n",
- " var id = ths.id.split('-')[0];\n",
- "\n",
- " var submission = ths.value;\n",
- " if (submission.indexOf('/') != -1) {\n",
- " var sub_parts = submission.split('/');\n",
- " //console.log(sub_parts);\n",
- " submission = sub_parts[0] / sub_parts[1];\n",
- " }\n",
- " //console.log(\"Reader entered\", submission);\n",
- "\n",
- " if (\"precision\" in ths.dataset) {\n",
- " var precision = ths.dataset.precision;\n",
- " submission = Number(Number(submission).toPrecision(precision));\n",
- " }\n",
- "\n",
- "\n",
- " //console.log(\"In check_numeric(), id=\"+id);\n",
- " //console.log(event.srcElement.id) \n",
- " //console.log(event.srcElement.dataset.feedback)\n",
- "\n",
- " var fb = document.getElementById(\"fb\" + id);\n",
- " fb.style.display = \"none\";\n",
- " fb.innerHTML = \"Incorrect -- try again.\";\n",
- "\n",
- " var answers = JSON.parse(ths.dataset.answers);\n",
- " //console.log(answers);\n",
- "\n",
- " var defaultFB = \"Incorrect. Try again.\";\n",
- " var correct;\n",
- " var done = false;\n",
- " answers.every(answer => {\n",
- " //console.log(answer.type);\n",
- "\n",
- " correct = false;\n",
- " // if (answer.type==\"value\"){\n",
- " if ('value' in answer) {\n",
- " if (submission == answer.value) {\n",
- " if (\"feedback\" in answer) {\n",
- " fb.innerHTML = jaxify(answer.feedback);\n",
- " } else {\n",
- " fb.innerHTML = jaxify(\"Correct\");\n",
- " }\n",
- " correct = answer.correct;\n",
- " //console.log(answer.correct);\n",
- " done = true;\n",
- " }\n",
- " // } else if (answer.type==\"range\") {\n",
- " } else if ('range' in answer) {\n",
- " console.log(answer.range);\n",
- " console.log(submission, submission >=answer.range[0], submission < answer.range[1])\n",
- " if ((submission >= answer.range[0]) && (submission < answer.range[1])) {\n",
- " fb.innerHTML = jaxify(answer.feedback);\n",
- " correct = answer.correct;\n",
- " console.log(answer.correct);\n",
- " done = true;\n",
- " }\n",
- " } else if (answer.type == \"default\") {\n",
- " if (\"feedback\" in answer) {\n",
- " defaultFB = answer.feedback;\n",
- " } \n",
- " }\n",
- " if (done) {\n",
- " return false; // Break out of loop if this has been marked correct\n",
- " } else {\n",
- " return true; // Keep looking for case that includes this as a correct answer\n",
- " }\n",
- " });\n",
- " console.log(\"done:\", done);\n",
- "\n",
- " if ((!done) && (defaultFB != \"\")) {\n",
- " fb.innerHTML = jaxify(defaultFB);\n",
- " //console.log(\"Default feedback\", defaultFB);\n",
- " }\n",
- "\n",
- " fb.style.display = \"block\";\n",
- " if (correct) {\n",
- " ths.className = \"Input-text\";\n",
- " ths.classList.add(\"correctButton\");\n",
- " fb.className = \"Feedback\";\n",
- " fb.classList.add(\"correct\");\n",
- " } else {\n",
- " ths.className = \"Input-text\";\n",
- " ths.classList.add(\"incorrectButton\");\n",
- " fb.className = \"Feedback\";\n",
- " fb.classList.add(\"incorrect\");\n",
- " }\n",
- "\n",
- " // What follows is for the saved responses stuff\n",
- " var outerContainer = fb.parentElement.parentElement;\n",
- " var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n",
- " if (responsesContainer) {\n",
- " console.log(submission);\n",
- " var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n",
- " //console.log(\"Question \" + qnum);\n",
- " //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n",
- " var responses=JSON.parse(responsesContainer.dataset.responses);\n",
- " console.log(responses);\n",
- " if (submission == ths.value){\n",
- " responses[qnum]= submission;\n",
- " } else {\n",
- " responses[qnum]= ths.value + \"(\" + submission +\")\";\n",
- " }\n",
- " responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n",
- " printResponses(responsesContainer);\n",
- " }\n",
- " // End code to preserve responses\n",
- "\n",
- " if (typeof MathJax != 'undefined') {\n",
- " var version = MathJax.version;\n",
- " console.log('MathJax version', version);\n",
- " if (version[0] == \"2\") {\n",
- " MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n",
- " } else if (version[0] == \"3\") {\n",
- " MathJax.typeset([fb]);\n",
- " }\n",
- " } else {\n",
- " console.log('MathJax not detected');\n",
- " }\n",
- " return false;\n",
- " }\n",
- "\n",
- "}\n",
- "\n",
- "function isValid(el, charC) {\n",
- " //console.log(\"Input char: \", charC);\n",
- " if (charC == 46) {\n",
- " if (el.value.indexOf('.') === -1) {\n",
- " return true;\n",
- " } else if (el.value.indexOf('/') != -1) {\n",
- " var parts = el.value.split('/');\n",
- " if (parts[1].indexOf('.') === -1) {\n",
- " return true;\n",
- " }\n",
- " }\n",
- " else {\n",
- " return false;\n",
- " }\n",
- " } else if (charC == 47) {\n",
- " if (el.value.indexOf('/') === -1) {\n",
- " if ((el.value != \"\") && (el.value != \".\")) {\n",
- " return true;\n",
- " } else {\n",
- " return false;\n",
- " }\n",
- " } else {\n",
- " return false;\n",
- " }\n",
- " } else if (charC == 45) {\n",
- " var edex = el.value.indexOf('e');\n",
- " if (edex == -1) {\n",
- " edex = el.value.indexOf('E');\n",
- " }\n",
- "\n",
- " if (el.value == \"\") {\n",
- " return true;\n",
- " } else if (edex == (el.value.length - 1)) { // If just after e or E\n",
- " return true;\n",
- " } else {\n",
- " return false;\n",
- " }\n",
- " } else if (charC == 101) { // \"e\"\n",
- " if ((el.value.indexOf('e') === -1) && (el.value.indexOf('E') === -1) && (el.value.indexOf('/') == -1)) {\n",
- " // Prev symbol must be digit or decimal point:\n",
- " if (el.value.slice(-1).search(/\\d/) >= 0) {\n",
- " return true;\n",
- " } else if (el.value.slice(-1).search(/\\./) >= 0) {\n",
- " return true;\n",
- " } else {\n",
- " return false;\n",
- " }\n",
- " } else {\n",
- " return false;\n",
- " }\n",
- " } else {\n",
- " if (charC > 31 && (charC < 48 || charC > 57))\n",
- " return false;\n",
- " }\n",
- " return true;\n",
- "}\n",
- "\n",
- "function numeric_keypress(evnt) {\n",
- " var charC = (evnt.which) ? evnt.which : evnt.keyCode;\n",
- "\n",
- " if (charC == 13) {\n",
- " check_numeric(this, evnt);\n",
- " } else {\n",
- " return isValid(this, charC);\n",
- " }\n",
- "}\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "function make_numeric(qa, outerqDiv, qDiv, aDiv, id) {\n",
- "\n",
- "\n",
- "\n",
- " //console.log(answer);\n",
- "\n",
- "\n",
- " outerqDiv.className = \"NumericQn\";\n",
- " aDiv.style.display = 'block';\n",
- "\n",
- " var lab = document.createElement(\"label\");\n",
- " lab.className = \"InpLabel\";\n",
- " lab.innerHTML = \"Type numeric answer here:\";\n",
- " aDiv.append(lab);\n",
- "\n",
- " var inp = document.createElement(\"input\");\n",
- " inp.type = \"text\";\n",
- " //inp.id=\"input-\"+id;\n",
- " inp.id = id + \"-0\";\n",
- " inp.className = \"Input-text\";\n",
- " inp.setAttribute('data-answers', JSON.stringify(qa.answers));\n",
- " if (\"precision\" in qa) {\n",
- " inp.setAttribute('data-precision', qa.precision);\n",
- " }\n",
- " aDiv.append(inp);\n",
- " //console.log(inp);\n",
- "\n",
- " //inp.addEventListener(\"keypress\", check_numeric);\n",
- " //inp.addEventListener(\"keypress\", numeric_keypress);\n",
- " /*\n",
- " inp.addEventListener(\"keypress\", function(event) {\n",
- " return numeric_keypress(this, event);\n",
- " }\n",
- " );\n",
- " */\n",
- " //inp.onkeypress=\"return numeric_keypress(this, event)\";\n",
- " inp.onkeypress = numeric_keypress;\n",
- " inp.onpaste = event => false;\n",
- "\n",
- " inp.addEventListener(\"focus\", function (event) {\n",
- " this.value = \"\";\n",
- " return false;\n",
- " }\n",
- " );\n",
- "\n",
- "\n",
- "}\n",
- "function jaxify(string) {\n",
- " var mystring = string;\n",
- "\n",
- " var count = 0;\n",
- " var loc = mystring.search(/([^\\\\]|^)(\\$)/);\n",
- "\n",
- " var count2 = 0;\n",
- " var loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n",
- "\n",
- " //console.log(loc);\n",
- "\n",
- " while ((loc >= 0) || (loc2 >= 0)) {\n",
- "\n",
- " /* Have to replace all the double $$ first with current implementation */\n",
- " if (loc2 >= 0) {\n",
- " if (count2 % 2 == 0) {\n",
- " mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\[\");\n",
- " } else {\n",
- " mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\]\");\n",
- " }\n",
- " count2++;\n",
- " } else {\n",
- " if (count % 2 == 0) {\n",
- " mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\(\");\n",
- " } else {\n",
- " mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\)\");\n",
- " }\n",
- " count++;\n",
- " }\n",
- " loc = mystring.search(/([^\\\\]|^)(\\$)/);\n",
- " loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n",
- " //console.log(mystring,\", loc:\",loc,\", loc2:\",loc2);\n",
- " }\n",
- "\n",
- " // repace markdown style links with actual links\n",
- " mystring = mystring.replace(/<(.*?)>/, '$1');\n",
- " mystring = mystring.replace(/\\[(.*?)\\]\\((.*?)\\)/, '$1');\n",
- "\n",
- " //console.log(mystring);\n",
- " return mystring;\n",
- "}\n",
- "\n",
- "\n",
- "function show_questions(json, mydiv) {\n",
- " console.log('show_questions');\n",
- " //var mydiv=document.getElementById(myid);\n",
- " var shuffle_questions = mydiv.dataset.shufflequestions;\n",
- " var num_questions = mydiv.dataset.numquestions;\n",
- " var shuffle_answers = mydiv.dataset.shuffleanswers;\n",
- " var max_width = mydiv.dataset.maxwidth;\n",
- "\n",
- " if (num_questions > json.length) {\n",
- " num_questions = json.length;\n",
- " }\n",
- "\n",
- " var questions;\n",
- " if ((num_questions < json.length) || (shuffle_questions == \"True\")) {\n",
- " //console.log(num_questions+\",\"+json.length);\n",
- " questions = getRandomSubarray(json, num_questions);\n",
- " } else {\n",
- " questions = json;\n",
- " }\n",
- "\n",
- " //console.log(\"SQ: \"+shuffle_questions+\", NQ: \" + num_questions + \", SA: \", shuffle_answers);\n",
- "\n",
- " // Iterate over questions\n",
- " questions.forEach((qa, index, array) => {\n",
- " //console.log(qa.question); \n",
- "\n",
- " var id = makeid(8);\n",
- " //console.log(id);\n",
- "\n",
- "\n",
- " // Create Div to contain question and answers\n",
- " var iDiv = document.createElement('div');\n",
- " //iDiv.id = 'quizWrap' + id + index;\n",
- " iDiv.id = 'quizWrap' + id;\n",
- " iDiv.className = 'Quiz';\n",
- " iDiv.setAttribute('data-qnum', index);\n",
- " iDiv.style.maxWidth =max_width+\"px\";\n",
- " mydiv.appendChild(iDiv);\n",
- " // iDiv.innerHTML=qa.question;\n",
- " \n",
- " var outerqDiv = document.createElement('div');\n",
- " outerqDiv.id = \"OuterquizQn\" + id + index;\n",
- " // Create div to contain question part\n",
- " var qDiv = document.createElement('div');\n",
- " qDiv.id = \"quizQn\" + id + index;\n",
- " \n",
- " if (qa.question) {\n",
- " iDiv.append(outerqDiv);\n",
- "\n",
- " //qDiv.textContent=qa.question;\n",
- " qDiv.innerHTML = jaxify(qa.question);\n",
- " outerqDiv.append(qDiv);\n",
- " }\n",
- "\n",
- " // Create div for code inside question\n",
- " var codeDiv;\n",
- " if (\"code\" in qa) {\n",
- " codeDiv = document.createElement('div');\n",
- " codeDiv.id = \"code\" + id + index;\n",
- " codeDiv.className = \"QuizCode\";\n",
- " var codePre = document.createElement('pre');\n",
- " codeDiv.append(codePre);\n",
- " var codeCode = document.createElement('code');\n",
- " codePre.append(codeCode);\n",
- " codeCode.innerHTML = qa.code;\n",
- " outerqDiv.append(codeDiv);\n",
- " //console.log(codeDiv);\n",
- " }\n",
- "\n",
- "\n",
- " // Create div to contain answer part\n",
- " var aDiv = document.createElement('div');\n",
- " aDiv.id = \"quizAns\" + id + index;\n",
- " aDiv.className = 'Answer';\n",
- " iDiv.append(aDiv);\n",
- "\n",
- " //console.log(qa.type);\n",
- "\n",
- " var num_correct;\n",
- " if ((qa.type == \"multiple_choice\") || (qa.type == \"many_choice\") ) {\n",
- " num_correct = make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id);\n",
- " if (\"answer_cols\" in qa) {\n",
- " //aDiv.style.gridTemplateColumns = 'auto '.repeat(qa.answer_cols);\n",
- " aDiv.style.gridTemplateColumns = 'repeat(' + qa.answer_cols + ', 1fr)';\n",
- " }\n",
- " } else if (qa.type == \"numeric\") {\n",
- " //console.log(\"numeric\");\n",
- " make_numeric(qa, outerqDiv, qDiv, aDiv, id);\n",
- " }\n",
- "\n",
- "\n",
- " //Make div for feedback\n",
- " var fb = document.createElement(\"div\");\n",
- " fb.id = \"fb\" + id;\n",
- " //fb.style=\"font-size: 20px;text-align:center;\";\n",
- " fb.className = \"Feedback\";\n",
- " fb.setAttribute(\"data-answeredcorrect\", 0);\n",
- " fb.setAttribute(\"data-numcorrect\", num_correct);\n",
- " iDiv.append(fb);\n",
- "\n",
- "\n",
- " });\n",
- " var preserveResponses = mydiv.dataset.preserveresponses;\n",
- " console.log(preserveResponses);\n",
- " console.log(preserveResponses == \"true\");\n",
- " if (preserveResponses == \"true\") {\n",
- " console.log(preserveResponses);\n",
- " // Create Div to contain record of answers\n",
- " var iDiv = document.createElement('div');\n",
- " iDiv.id = 'responses' + mydiv.id;\n",
- " iDiv.className = 'JCResponses';\n",
- " // Create a place to store responses as an empty array\n",
- " iDiv.setAttribute('data-responses', '[]');\n",
- "\n",
- " // Dummy Text\n",
- " iDiv.innerHTML=\"Select your answers and then follow the directions that will appear here.\"\n",
- " //iDiv.className = 'Quiz';\n",
- " mydiv.appendChild(iDiv);\n",
- " }\n",
- "//console.log(\"At end of show_questions\");\n",
- " if (typeof MathJax != 'undefined') {\n",
- " console.log(\"MathJax version\", MathJax.version);\n",
- " var version = MathJax.version;\n",
- " setTimeout(function(){\n",
- " var version = MathJax.version;\n",
- " console.log('After sleep, MathJax version', version);\n",
- " if (version[0] == \"2\") {\n",
- " MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n",
- " } else if (version[0] == \"3\") {\n",
- " if (MathJax.hasOwnProperty('typeset') ) {\n",
- " MathJax.typeset([mydiv]);\n",
- " } else {\n",
- " console.log('WARNING: Trying to force load MathJax 3');\n",
- " window.MathJax = {\n",
- " tex: {\n",
- " inlineMath: [['$', '$'], ['\\\\(', '\\\\)']]\n",
- " },\n",
- " svg: {\n",
- " fontCache: 'global'\n",
- " }\n",
- " };\n",
- "\n",
- " (function () {\n",
- " var script = document.createElement('script');\n",
- " script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js';\n",
- " script.async = true;\n",
- " document.head.appendChild(script);\n",
- " })();\n",
- " }\n",
- " }\n",
- " }, 500);\n",
- "if (typeof version == 'undefined') {\n",
- " } else\n",
- " {\n",
- " if (version[0] == \"2\") {\n",
- " MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n",
- " } else if (version[0] == \"3\") {\n",
- " if (MathJax.hasOwnProperty('typeset') ) {\n",
- " MathJax.typeset([mydiv]);\n",
- " } else {\n",
- " console.log('WARNING: Trying to force load MathJax 3');\n",
- " window.MathJax = {\n",
- " tex: {\n",
- " inlineMath: [['$', '$'], ['\\\\(', '\\\\)']]\n",
- " },\n",
- " svg: {\n",
- " fontCache: 'global'\n",
- " }\n",
- " };\n",
- "\n",
- " (function () {\n",
- " var script = document.createElement('script');\n",
- " script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js';\n",
- " script.async = true;\n",
- " document.head.appendChild(script);\n",
- " })();\n",
- " }\n",
- " } else {\n",
- " console.log(\"MathJax not found\");\n",
- " }\n",
- " }\n",
- " }\n",
- "\n",
- " // stop event propagation for the .Link class\n",
- " var links = document.getElementsByClassName('Link')\n",
- " for (var i = 0; i < links.length; i++) {\n",
- " links[i].addEventListener('click', function(e){\n",
- " e.stopPropagation();\n",
- " });\n",
- " }\n",
- "\n",
- " return false;\n",
- "}\n",
- "/* This is to handle asynchrony issues in loading Jupyter notebooks\n",
- " where the quiz has been previously run. The Javascript was generally\n",
- " being run before the div was added to the DOM. I tried to do this\n",
- " more elegantly using Mutation Observer, but I didn't get it to work.\n",
- "\n",
- " Someone more knowledgeable could make this better ;-) */\n",
- "\n",
- " function try_show() {\n",
- " if(document.getElementById(\"KIRfHxXXCJUU\")) {\n",
- " show_questions(questionsKIRfHxXXCJUU, KIRfHxXXCJUU); \n",
- " } else {\n",
- " setTimeout(try_show, 200);\n",
- " }\n",
- " };\n",
- " \n",
- " {\n",
- " // console.log(element);\n",
- "\n",
- " //console.log(\"KIRfHxXXCJUU\");\n",
- " // console.log(document.getElementById(\"KIRfHxXXCJUU\"));\n",
- "\n",
- " try_show();\n",
- " }\n",
- " "
- ],
+ "application/javascript": "var questionsKIRfHxXXCJUU=[\n {\n \"question\": \"What is the standard form for a system of linear equations?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"Ax = b\",\n \"correct\": true,\n \"feedback\": \"Correct! A system of linear equations is written in standard form as Ax = b, where A is the coefficient matrix, x is the vector of unknowns, and b is the right-hand side vector.\"\n },\n {\n \"answer\": \"x = Ab\",\n \"correct\": false,\n \"feedback\": \"Incorrect. The standard form is Ax = b, not x = Ab.\"\n },\n {\n \"answer\": \"x = A^(-1)b\",\n \"correct\": false,\n \"feedback\": \"Incorrect. This is the solution to a system of linear equations (x = A^(-1)b), not the standard form of the system itself.\"\n },\n {\n \"answer\": \"b = xA\",\n \"correct\": false,\n \"feedback\": \"Incorrect. The standard form of a system of linear equations is Ax = b, not b = xA.\"\n }\n ]\n },\n {\n \"question\": \"When is the solution to a system of linear equations Ax = b unique?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"When A is square and nonsingular (det(A) ≠ 0)\",\n \"correct\": true,\n \"feedback\": \"Correct! A unique solution exists when A is a square matrix and has a non-zero determinant, meaning its rows or columns are linearly independent.\"\n },\n {\n \"answer\": \"When A is any rectangular matrix\",\n \"correct\": false,\n \"feedback\": \"Incorrect. Rectangular matrices (more equations than unknowns or vice versa) typically lead to either no solution or infinitely many solutions.\"\n },\n {\n \"answer\": \"When A is a singular matrix\",\n \"correct\": false,\n \"feedback\": \"Incorrect. A singular matrix (det(A) = 0) means the system either has no solution or infinitely many solutions.\"\n },\n {\n \"answer\": \"When A is a diagonal matrix\",\n \"correct\": false,\n \"feedback\": \"Incorrect. While diagonal matrices with non-zero diagonal elements do have unique solutions, this is not a necessary condition. Any nonsingular matrix will have a unique solution.\"\n }\n ]\n },\n {\n \"question\": \"Which of the following is the recommended way to solve a system of linear equations in Python?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"np.linalg.solve(A, b)\",\n \"correct\": true,\n \"feedback\": \"Correct! Using np.linalg.solve is the recommended approach as it's more numerically stable and computationally efficient than computing the inverse.\"\n },\n {\n \"answer\": \"np.linalg.inv(A) @ b\",\n \"correct\": false,\n \"feedback\": \"Incorrect. While this will work mathematically, it's not recommended because matrix inversion is numerically unstable and computationally expensive.\"\n },\n {\n \"answer\": \"A * b\",\n \"correct\": false,\n \"feedback\": \"Incorrect. This would perform element-wise multiplication, not solve the system of equations.\"\n },\n {\n \"answer\": \"A @ b\",\n \"correct\": false,\n \"feedback\": \"Incorrect. This performs matrix multiplication, not solving for x in Ax = b.\"\n }\n ]\n },\n {\n \"question\": \"What is the main advantage of LU decomposition for solving linear systems?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"It allows efficient solving for multiple right-hand sides after a single decomposition\",\n \"correct\": true,\n \"feedback\": \"Correct! Once the LU decomposition is computed, it can be reused to solve for different right-hand side vectors without repeating the most expensive part of the computation.\"\n },\n {\n \"answer\": \"It always guarantees a solution even for singular matrices\",\n \"correct\": false,\n \"feedback\": \"Incorrect. LU decomposition does not guarantee solutions for singular matrices.\"\n },\n {\n \"answer\": \"It's the only method that works with sparse matrices\",\n \"correct\": false,\n \"feedback\": \"Incorrect. There are several methods that work with sparse matrices, including iterative methods.\"\n },\n {\n \"answer\": \"It requires less memory than other solution methods\",\n \"correct\": false,\n \"feedback\": \"Incorrect. While memory efficiency isn't the main advantage, the key benefit is computational efficiency when solving multiple systems with the same coefficient matrix.\"\n }\n ]\n },\n {\n \"question\": \"What makes a matrix 'sparse'?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"It contains many zero elements\",\n \"correct\": true,\n \"feedback\": \"Correct! A sparse matrix has many zero elements, allowing for specialized storage formats and algorithms that improve efficiency.\"\n },\n {\n \"answer\": \"It has more columns than rows\",\n \"correct\": false,\n \"feedback\": \"Incorrect. A matrix with more columns than rows is called a wide matrix or has a rectangular shape, but this doesn't make it sparse.\"\n },\n {\n \"answer\": \"Its determinant is close to zero\",\n \"correct\": false,\n \"feedback\": \"Incorrect. A matrix with a determinant close to zero is called ill-conditioned, not sparse.\"\n },\n {\n \"answer\": \"It contains only integer values\",\n \"correct\": false,\n \"feedback\": \"Incorrect. The type of values in a matrix doesn't determine whether it's sparse. Sparsity refers to the number of zero elements.\"\n }\n ]\n },\n {\n \"question\": \"Which of the following SciPy sparse matrix formats is most efficient for incrementally building a matrix?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"dok_matrix\",\n \"correct\": true,\n \"feedback\": \"Correct! dok_matrix (Dictionary of Keys) is based on a hash table and allows efficient random access, making it ideal for incrementally building a matrix.\"\n },\n {\n \"answer\": \"csr_matrix\",\n \"correct\": false,\n \"feedback\": \"Incorrect. While csr_matrix (Compressed Sparse Row) is efficient for arithmetic operations and row slicing, it's not the best choice for incrementally building a matrix.\"\n },\n {\n \"answer\": \"csc_matrix\",\n \"correct\": false,\n \"feedback\": \"Incorrect. csc_matrix (Compressed Sparse Column) is efficient for arithmetic operations and column slicing, but not for incrementally building a matrix.\"\n },\n {\n \"answer\": \"dia_matrix\",\n \"correct\": false,\n \"feedback\": \"Incorrect. dia_matrix is efficient for storing diagonal matrices but isn't designed for incrementally building general matrices.\"\n }\n ]\n },\n {\n \"question\": \"Which function in SciPy should be used to solve sparse linear systems?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"scipy.sparse.linalg.spsolve\",\n \"correct\": true,\n \"feedback\": \"Correct! spsolve is specifically designed to solve sparse linear systems efficiently.\"\n },\n {\n \"answer\": \"scipy.linalg.solve\",\n \"correct\": false,\n \"feedback\": \"Incorrect. scipy.linalg.solve is designed for dense matrices and would not take advantage of the sparsity structure.\"\n },\n {\n \"answer\": \"scipy.sparse.solve\",\n \"correct\": false,\n \"feedback\": \"Incorrect. This is not a valid function in SciPy. The correct function is scipy.sparse.linalg.spsolve.\"\n },\n {\n \"answer\": \"scipy.sparse.linalg.eigs\",\n \"correct\": false,\n \"feedback\": \"Incorrect. eigs computes eigenvalues and eigenvectors, not solutions to linear systems.\"\n }\n ]\n },\n {\n \"question\": \"Which sparse solver has shown superior performance for electrical circuit problems?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"KLU\",\n \"correct\": true,\n \"feedback\": \"Correct! As mentioned in the notebook, KLU (Kent Clark LU) shows superior performance for electrical circuit problems, including power systems.\"\n },\n {\n \"answer\": \"SuperLU\",\n \"correct\": false,\n \"feedback\": \"Incorrect. While SuperLU is a good general-purpose sparse solver, KLU is specifically optimized for circuit problems.\"\n },\n {\n \"answer\": \"UMFPACK\",\n \"correct\": false,\n \"feedback\": \"Incorrect. UMFPACK is another good general-purpose sparse solver, but it's not specifically optimized for electrical circuit problems like KLU.\"\n },\n {\n \"answer\": \"LAPACK\",\n \"correct\": false,\n \"feedback\": \"Incorrect. LAPACK is primarily for dense linear algebra, not sparse systems.\"\n }\n ]\n },\n {\n \"question\": \"Why is it not recommended to use matrix inversion (A⁻¹) to solve linear systems?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"It's numerically unstable and computationally expensive\",\n \"correct\": true,\n \"feedback\": \"Correct! Matrix inversion is both numerically less stable and computationally more expensive than direct solving methods like LU decomposition.\"\n },\n {\n \"answer\": \"It only works for square matrices\",\n \"correct\": false,\n \"feedback\": \"Incorrect. While it's true that only square matrices can be inverted, this isn't the main reason to avoid using inversion for solving systems.\"\n },\n {\n \"answer\": \"It's not supported in NumPy\",\n \"correct\": false,\n \"feedback\": \"Incorrect. NumPy does support matrix inversion with np.linalg.inv(), but it's still not recommended for solving systems.\"\n },\n {\n \"answer\": \"It only works for real-valued matrices\",\n \"correct\": false,\n \"feedback\": \"Incorrect. Matrix inversion works for complex-valued matrices as well, but is still not the recommended approach.\"\n }\n ]\n },\n {\n \"question\": \"What is the primary advantage of using a sparse matrix representation for power system admittance matrices?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"Better efficiency and less memory usage\",\n \"correct\": true,\n \"feedback\": \"Correct! Power system admittance matrices are naturally sparse because buses are only connected to a few other buses. Using sparse representations provides better computational efficiency and significantly reduced memory usage.\"\n },\n {\n \"answer\": \"More accurate results in power flow calculations\",\n \"correct\": false,\n \"feedback\": \"Incorrect. Sparse matrix representation affects efficiency, not accuracy. The mathematical results should be identical to using dense matrices.\"\n },\n {\n \"answer\": \"Ability to handle complex numbers\",\n \"correct\": false,\n \"feedback\": \"Incorrect. Both sparse and dense matrices in Python can handle complex numbers for admittance matrices.\"\n },\n {\n \"answer\": \"Simplified matrix construction\",\n \"correct\": false,\n \"feedback\": \"Incorrect. Sparse matrices can actually be more complex to construct, but the efficiency benefits outweigh this complexity for large power systems.\"\n }\n ]\n }\n]\n;\n // Make a random ID\nfunction makeid(length) {\n var result = [];\n var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\n var charactersLength = characters.length;\n for (var i = 0; i < length; i++) {\n result.push(characters.charAt(Math.floor(Math.random() * charactersLength)));\n }\n return result.join('');\n}\n\n// Choose a random subset of an array. Can also be used to shuffle the array\nfunction getRandomSubarray(arr, size) {\n var shuffled = arr.slice(0), i = arr.length, temp, index;\n while (i--) {\n index = Math.floor((i + 1) * Math.random());\n temp = shuffled[index];\n shuffled[index] = shuffled[i];\n shuffled[i] = temp;\n }\n return shuffled.slice(0, size);\n}\n\nfunction printResponses(responsesContainer) {\n var responses=JSON.parse(responsesContainer.dataset.responses);\n var stringResponses='IMPORTANT!To preserve this answer sequence for submission, when you have finalized your answers: - Copy the text in this cell below \"Answer String\"
- Double click on the cell directly below the Answer String, labeled \"Replace Me\"
- Select the whole \"Replace Me\" text
- Paste in your answer string and press shift-Enter.
- Save the notebook using the save icon or File->Save Notebook menu item
Answer String:
';\n console.log(responses);\n responses.forEach((response, index) => {\n if (response) {\n console.log(index + ': ' + response);\n stringResponses+= index + ': ' + response +\"
\";\n }\n });\n responsesContainer.innerHTML=stringResponses;\n}\n/* Callback function to determine whether a selected multiple-choice\n button corresponded to a correct answer and to provide feedback\n based on the answer */\nfunction check_mc() {\n var id = this.id.split('-')[0];\n //var response = this.id.split('-')[1];\n //console.log(response);\n //console.log(\"In check_mc(), id=\"+id);\n //console.log(event.srcElement.id) \n //console.log(event.srcElement.dataset.correct) \n //console.log(event.srcElement.dataset.feedback)\n\n var label = event.srcElement;\n //console.log(label, label.nodeName);\n var depth = 0;\n while ((label.nodeName != \"LABEL\") && (depth < 20)) {\n label = label.parentElement;\n console.log(depth, label);\n depth++;\n }\n\n\n\n var answers = label.parentElement.children;\n //console.log(answers);\n\n // Split behavior based on multiple choice vs many choice:\n var fb = document.getElementById(\"fb\" + id);\n\n\n\n /* Multiple choice (1 answer). Allow for 0 correct\n answers as an edge case */\n if (fb.dataset.numcorrect <= 1) {\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n //console.log(responsesContainer);\n var response = label.firstChild.innerText;\n if (label.querySelector(\".QuizCode\")){\n response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n }\n console.log(response);\n //console.log(document.getElementById(\"quizWrap\"+id));\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n console.log(responses);\n responses[qnum]= response;\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End code to preserve responses\n\n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n //console.log(child);\n child.className = \"MCButton\";\n }\n\n\n\n if (label.dataset.correct == \"true\") {\n // console.log(\"Correct action\");\n if (\"feedback\" in label.dataset) {\n fb.innerHTML = jaxify(label.dataset.feedback);\n } else {\n fb.innerHTML = \"Correct!\";\n }\n label.classList.add(\"correctButton\");\n\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n\n } else {\n if (\"feedback\" in label.dataset) {\n fb.innerHTML = jaxify(label.dataset.feedback);\n } else {\n fb.innerHTML = \"Incorrect -- try again.\";\n }\n //console.log(\"Error action\");\n label.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n }\n else { /* Many choice (more than 1 correct answer) */\n var reset = false;\n var feedback;\n if (label.dataset.correct == \"true\") {\n if (\"feedback\" in label.dataset) {\n feedback = jaxify(label.dataset.feedback);\n } else {\n feedback = \"Correct!\";\n }\n if (label.dataset.answered <= 0) {\n if (fb.dataset.answeredcorrect < 0) {\n fb.dataset.answeredcorrect = 1;\n reset = true;\n } else {\n fb.dataset.answeredcorrect++;\n }\n if (reset) {\n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n child.className = \"MCButton\";\n child.dataset.answered = 0;\n }\n }\n label.classList.add(\"correctButton\");\n label.dataset.answered = 1;\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n\n }\n } else {\n if (\"feedback\" in label.dataset) {\n feedback = jaxify(label.dataset.feedback);\n } else {\n feedback = \"Incorrect -- try again.\";\n }\n if (fb.dataset.answeredcorrect > 0) {\n fb.dataset.answeredcorrect = -1;\n reset = true;\n } else {\n fb.dataset.answeredcorrect--;\n }\n\n if (reset) {\n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n child.className = \"MCButton\";\n child.dataset.answered = 0;\n }\n }\n label.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n //console.log(responsesContainer);\n var response = label.firstChild.innerText;\n if (label.querySelector(\".QuizCode\")){\n response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n }\n console.log(response);\n //console.log(document.getElementById(\"quizWrap\"+id));\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n if (label.dataset.correct == \"true\") {\n if (typeof(responses[qnum]) == \"object\"){\n if (!responses[qnum].includes(response))\n responses[qnum].push(response);\n } else{\n responses[qnum]= [ response ];\n }\n } else {\n responses[qnum]= response;\n }\n console.log(responses);\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End save responses stuff\n\n\n\n var numcorrect = fb.dataset.numcorrect;\n var answeredcorrect = fb.dataset.answeredcorrect;\n if (answeredcorrect >= 0) {\n fb.innerHTML = feedback + \" [\" + answeredcorrect + \"/\" + numcorrect + \"]\";\n } else {\n fb.innerHTML = feedback + \" [\" + 0 + \"/\" + numcorrect + \"]\";\n }\n\n\n }\n\n if (typeof MathJax != 'undefined') {\n var version = MathJax.version;\n console.log('MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([fb]);\n }\n } else {\n console.log('MathJax not detected');\n }\n\n}\n\n\n/* Function to produce the HTML buttons for a multiple choice/\n many choice question and to update the CSS tags based on\n the question type */\nfunction make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id) {\n\n var shuffled;\n if (shuffle_answers == \"True\") {\n //console.log(shuffle_answers+\" read as true\");\n shuffled = getRandomSubarray(qa.answers, qa.answers.length);\n } else {\n //console.log(shuffle_answers+\" read as false\");\n shuffled = qa.answers;\n }\n\n\n var num_correct = 0;\n\n shuffled.forEach((item, index, ans_array) => {\n //console.log(answer);\n\n // Make input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.style = \"display:none;\";\n aDiv.append(inp);\n\n //Make label for input element\n var lab = document.createElement(\"label\");\n lab.className = \"MCButton\";\n lab.id = id + '-' + index;\n lab.onclick = check_mc;\n var aSpan = document.createElement('span');\n aSpan.classsName = \"\";\n //qDiv.id=\"quizQn\"+id+index;\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n //aSpan.innerHTML=item.answer;\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n var codeSpan;\n if (\"code\" in item) {\n codeSpan = document.createElement('span');\n codeSpan.id = \"code\" + id + index;\n codeSpan.className = \"QuizCode\";\n var codePre = document.createElement('pre');\n codeSpan.append(codePre);\n var codeCode = document.createElement('code');\n codePre.append(codeCode);\n codeCode.innerHTML = item.code;\n lab.append(codeSpan);\n //console.log(codeSpan);\n }\n\n //lab.textContent=item.answer;\n\n // Set the data attributes for the answer\n lab.setAttribute('data-correct', item.correct);\n if (item.correct) {\n num_correct++;\n }\n if (\"feedback\" in item) {\n lab.setAttribute('data-feedback', item.feedback);\n }\n lab.setAttribute('data-answered', 0);\n\n aDiv.append(lab);\n\n });\n\n if (num_correct > 1) {\n outerqDiv.className = \"ManyChoiceQn\";\n } else {\n outerqDiv.className = \"MultipleChoiceQn\";\n }\n\n return num_correct;\n\n}\nfunction check_numeric(ths, event) {\n\n if (event.keyCode === 13) {\n ths.blur();\n\n var id = ths.id.split('-')[0];\n\n var submission = ths.value;\n if (submission.indexOf('/') != -1) {\n var sub_parts = submission.split('/');\n //console.log(sub_parts);\n submission = sub_parts[0] / sub_parts[1];\n }\n //console.log(\"Reader entered\", submission);\n\n if (\"precision\" in ths.dataset) {\n var precision = ths.dataset.precision;\n submission = Number(Number(submission).toPrecision(precision));\n }\n\n\n //console.log(\"In check_numeric(), id=\"+id);\n //console.log(event.srcElement.id) \n //console.log(event.srcElement.dataset.feedback)\n\n var fb = document.getElementById(\"fb\" + id);\n fb.style.display = \"none\";\n fb.innerHTML = \"Incorrect -- try again.\";\n\n var answers = JSON.parse(ths.dataset.answers);\n //console.log(answers);\n\n var defaultFB = \"Incorrect. Try again.\";\n var correct;\n var done = false;\n answers.every(answer => {\n //console.log(answer.type);\n\n correct = false;\n // if (answer.type==\"value\"){\n if ('value' in answer) {\n if (submission == answer.value) {\n if (\"feedback\" in answer) {\n fb.innerHTML = jaxify(answer.feedback);\n } else {\n fb.innerHTML = jaxify(\"Correct\");\n }\n correct = answer.correct;\n //console.log(answer.correct);\n done = true;\n }\n // } else if (answer.type==\"range\") {\n } else if ('range' in answer) {\n console.log(answer.range);\n console.log(submission, submission >=answer.range[0], submission < answer.range[1])\n if ((submission >= answer.range[0]) && (submission < answer.range[1])) {\n fb.innerHTML = jaxify(answer.feedback);\n correct = answer.correct;\n console.log(answer.correct);\n done = true;\n }\n } else if (answer.type == \"default\") {\n if (\"feedback\" in answer) {\n defaultFB = answer.feedback;\n } \n }\n if (done) {\n return false; // Break out of loop if this has been marked correct\n } else {\n return true; // Keep looking for case that includes this as a correct answer\n }\n });\n console.log(\"done:\", done);\n\n if ((!done) && (defaultFB != \"\")) {\n fb.innerHTML = jaxify(defaultFB);\n //console.log(\"Default feedback\", defaultFB);\n }\n\n fb.style.display = \"block\";\n if (correct) {\n ths.className = \"Input-text\";\n ths.classList.add(\"correctButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n } else {\n ths.className = \"Input-text\";\n ths.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n console.log(submission);\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n //console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n console.log(responses);\n if (submission == ths.value){\n responses[qnum]= submission;\n } else {\n responses[qnum]= ths.value + \"(\" + submission +\")\";\n }\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End code to preserve responses\n\n if (typeof MathJax != 'undefined') {\n var version = MathJax.version;\n console.log('MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([fb]);\n }\n } else {\n console.log('MathJax not detected');\n }\n return false;\n }\n\n}\n\nfunction isValid(el, charC) {\n //console.log(\"Input char: \", charC);\n if (charC == 46) {\n if (el.value.indexOf('.') === -1) {\n return true;\n } else if (el.value.indexOf('/') != -1) {\n var parts = el.value.split('/');\n if (parts[1].indexOf('.') === -1) {\n return true;\n }\n }\n else {\n return false;\n }\n } else if (charC == 47) {\n if (el.value.indexOf('/') === -1) {\n if ((el.value != \"\") && (el.value != \".\")) {\n return true;\n } else {\n return false;\n }\n } else {\n return false;\n }\n } else if (charC == 45) {\n var edex = el.value.indexOf('e');\n if (edex == -1) {\n edex = el.value.indexOf('E');\n }\n\n if (el.value == \"\") {\n return true;\n } else if (edex == (el.value.length - 1)) { // If just after e or E\n return true;\n } else {\n return false;\n }\n } else if (charC == 101) { // \"e\"\n if ((el.value.indexOf('e') === -1) && (el.value.indexOf('E') === -1) && (el.value.indexOf('/') == -1)) {\n // Prev symbol must be digit or decimal point:\n if (el.value.slice(-1).search(/\\d/) >= 0) {\n return true;\n } else if (el.value.slice(-1).search(/\\./) >= 0) {\n return true;\n } else {\n return false;\n }\n } else {\n return false;\n }\n } else {\n if (charC > 31 && (charC < 48 || charC > 57))\n return false;\n }\n return true;\n}\n\nfunction numeric_keypress(evnt) {\n var charC = (evnt.which) ? evnt.which : evnt.keyCode;\n\n if (charC == 13) {\n check_numeric(this, evnt);\n } else {\n return isValid(this, charC);\n }\n}\n\n\n\n\n\nfunction make_numeric(qa, outerqDiv, qDiv, aDiv, id) {\n\n\n\n //console.log(answer);\n\n\n outerqDiv.className = \"NumericQn\";\n aDiv.style.display = 'block';\n\n var lab = document.createElement(\"label\");\n lab.className = \"InpLabel\";\n lab.innerHTML = \"Type numeric answer here:\";\n aDiv.append(lab);\n\n var inp = document.createElement(\"input\");\n inp.type = \"text\";\n //inp.id=\"input-\"+id;\n inp.id = id + \"-0\";\n inp.className = \"Input-text\";\n inp.setAttribute('data-answers', JSON.stringify(qa.answers));\n if (\"precision\" in qa) {\n inp.setAttribute('data-precision', qa.precision);\n }\n aDiv.append(inp);\n //console.log(inp);\n\n //inp.addEventListener(\"keypress\", check_numeric);\n //inp.addEventListener(\"keypress\", numeric_keypress);\n /*\n inp.addEventListener(\"keypress\", function(event) {\n return numeric_keypress(this, event);\n }\n );\n */\n //inp.onkeypress=\"return numeric_keypress(this, event)\";\n inp.onkeypress = numeric_keypress;\n inp.onpaste = event => false;\n\n inp.addEventListener(\"focus\", function (event) {\n this.value = \"\";\n return false;\n }\n );\n\n\n}\nfunction jaxify(string) {\n var mystring = string;\n\n var count = 0;\n var loc = mystring.search(/([^\\\\]|^)(\\$)/);\n\n var count2 = 0;\n var loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n\n //console.log(loc);\n\n while ((loc >= 0) || (loc2 >= 0)) {\n\n /* Have to replace all the double $$ first with current implementation */\n if (loc2 >= 0) {\n if (count2 % 2 == 0) {\n mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\[\");\n } else {\n mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\]\");\n }\n count2++;\n } else {\n if (count % 2 == 0) {\n mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\(\");\n } else {\n mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\)\");\n }\n count++;\n }\n loc = mystring.search(/([^\\\\]|^)(\\$)/);\n loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n //console.log(mystring,\", loc:\",loc,\", loc2:\",loc2);\n }\n\n // repace markdown style links with actual links\n mystring = mystring.replace(/<(.*?)>/, '$1');\n mystring = mystring.replace(/\\[(.*?)\\]\\((.*?)\\)/, '$1');\n\n //console.log(mystring);\n return mystring;\n}\n\n\nfunction show_questions(json, mydiv) {\n console.log('show_questions');\n //var mydiv=document.getElementById(myid);\n var shuffle_questions = mydiv.dataset.shufflequestions;\n var num_questions = mydiv.dataset.numquestions;\n var shuffle_answers = mydiv.dataset.shuffleanswers;\n var max_width = mydiv.dataset.maxwidth;\n\n if (num_questions > json.length) {\n num_questions = json.length;\n }\n\n var questions;\n if ((num_questions < json.length) || (shuffle_questions == \"True\")) {\n //console.log(num_questions+\",\"+json.length);\n questions = getRandomSubarray(json, num_questions);\n } else {\n questions = json;\n }\n\n //console.log(\"SQ: \"+shuffle_questions+\", NQ: \" + num_questions + \", SA: \", shuffle_answers);\n\n // Iterate over questions\n questions.forEach((qa, index, array) => {\n //console.log(qa.question); \n\n var id = makeid(8);\n //console.log(id);\n\n\n // Create Div to contain question and answers\n var iDiv = document.createElement('div');\n //iDiv.id = 'quizWrap' + id + index;\n iDiv.id = 'quizWrap' + id;\n iDiv.className = 'Quiz';\n iDiv.setAttribute('data-qnum', index);\n iDiv.style.maxWidth =max_width+\"px\";\n mydiv.appendChild(iDiv);\n // iDiv.innerHTML=qa.question;\n \n var outerqDiv = document.createElement('div');\n outerqDiv.id = \"OuterquizQn\" + id + index;\n // Create div to contain question part\n var qDiv = document.createElement('div');\n qDiv.id = \"quizQn\" + id + index;\n \n if (qa.question) {\n iDiv.append(outerqDiv);\n\n //qDiv.textContent=qa.question;\n qDiv.innerHTML = jaxify(qa.question);\n outerqDiv.append(qDiv);\n }\n\n // Create div for code inside question\n var codeDiv;\n if (\"code\" in qa) {\n codeDiv = document.createElement('div');\n codeDiv.id = \"code\" + id + index;\n codeDiv.className = \"QuizCode\";\n var codePre = document.createElement('pre');\n codeDiv.append(codePre);\n var codeCode = document.createElement('code');\n codePre.append(codeCode);\n codeCode.innerHTML = qa.code;\n outerqDiv.append(codeDiv);\n //console.log(codeDiv);\n }\n\n\n // Create div to contain answer part\n var aDiv = document.createElement('div');\n aDiv.id = \"quizAns\" + id + index;\n aDiv.className = 'Answer';\n iDiv.append(aDiv);\n\n //console.log(qa.type);\n\n var num_correct;\n if ((qa.type == \"multiple_choice\") || (qa.type == \"many_choice\") ) {\n num_correct = make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id);\n if (\"answer_cols\" in qa) {\n //aDiv.style.gridTemplateColumns = 'auto '.repeat(qa.answer_cols);\n aDiv.style.gridTemplateColumns = 'repeat(' + qa.answer_cols + ', 1fr)';\n }\n } else if (qa.type == \"numeric\") {\n //console.log(\"numeric\");\n make_numeric(qa, outerqDiv, qDiv, aDiv, id);\n }\n\n\n //Make div for feedback\n var fb = document.createElement(\"div\");\n fb.id = \"fb\" + id;\n //fb.style=\"font-size: 20px;text-align:center;\";\n fb.className = \"Feedback\";\n fb.setAttribute(\"data-answeredcorrect\", 0);\n fb.setAttribute(\"data-numcorrect\", num_correct);\n iDiv.append(fb);\n\n\n });\n var preserveResponses = mydiv.dataset.preserveresponses;\n console.log(preserveResponses);\n console.log(preserveResponses == \"true\");\n if (preserveResponses == \"true\") {\n console.log(preserveResponses);\n // Create Div to contain record of answers\n var iDiv = document.createElement('div');\n iDiv.id = 'responses' + mydiv.id;\n iDiv.className = 'JCResponses';\n // Create a place to store responses as an empty array\n iDiv.setAttribute('data-responses', '[]');\n\n // Dummy Text\n iDiv.innerHTML=\"Select your answers and then follow the directions that will appear here.\"\n //iDiv.className = 'Quiz';\n mydiv.appendChild(iDiv);\n }\n//console.log(\"At end of show_questions\");\n if (typeof MathJax != 'undefined') {\n console.log(\"MathJax version\", MathJax.version);\n var version = MathJax.version;\n setTimeout(function(){\n var version = MathJax.version;\n console.log('After sleep, MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n if (MathJax.hasOwnProperty('typeset') ) {\n MathJax.typeset([mydiv]);\n } else {\n console.log('WARNING: Trying to force load MathJax 3');\n window.MathJax = {\n tex: {\n inlineMath: [['$', '$'], ['\\\\(', '\\\\)']]\n },\n svg: {\n fontCache: 'global'\n }\n };\n\n (function () {\n var script = document.createElement('script');\n script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js';\n script.async = true;\n document.head.appendChild(script);\n })();\n }\n }\n }, 500);\nif (typeof version == 'undefined') {\n } else\n {\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n if (MathJax.hasOwnProperty('typeset') ) {\n MathJax.typeset([mydiv]);\n } else {\n console.log('WARNING: Trying to force load MathJax 3');\n window.MathJax = {\n tex: {\n inlineMath: [['$', '$'], ['\\\\(', '\\\\)']]\n },\n svg: {\n fontCache: 'global'\n }\n };\n\n (function () {\n var script = document.createElement('script');\n script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js';\n script.async = true;\n document.head.appendChild(script);\n })();\n }\n } else {\n console.log(\"MathJax not found\");\n }\n }\n }\n\n // stop event propagation for the .Link class\n var links = document.getElementsByClassName('Link')\n for (var i = 0; i < links.length; i++) {\n links[i].addEventListener('click', function(e){\n e.stopPropagation();\n });\n }\n\n return false;\n}\n/* This is to handle asynchrony issues in loading Jupyter notebooks\n where the quiz has been previously run. The Javascript was generally\n being run before the div was added to the DOM. I tried to do this\n more elegantly using Mutation Observer, but I didn't get it to work.\n\n Someone more knowledgeable could make this better ;-) */\n\n function try_show() {\n if(document.getElementById(\"KIRfHxXXCJUU\")) {\n show_questions(questionsKIRfHxXXCJUU, KIRfHxXXCJUU); \n } else {\n setTimeout(try_show, 200);\n }\n };\n \n {\n // console.log(element);\n\n //console.log(\"KIRfHxXXCJUU\");\n // console.log(document.getElementById(\"KIRfHxXXCJUU\"));\n\n try_show();\n }\n ",
"text/plain": [
""
]
@@ -2576,1100 +1490,7 @@
},
{
"data": {
- "application/javascript": [
- "var questionsENKrJXTGxdXc=[\n",
- " {\n",
- " \"question\": \"What is the standard form for a system of linear equations?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"Ax = b\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! A system of linear equations is written in standard form as Ax = b, where A is the coefficient matrix, x is the vector of unknowns, and b is the right-hand side vector.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"x = Ab\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. The standard form is Ax = b, not x = Ab.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"x = A^(-1)b\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. This is the solution to a system of linear equations (x = A^(-1)b), not the standard form of the system itself.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"b = xA\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. The standard form of a system of linear equations is Ax = b, not b = xA.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"When is the solution to a system of linear equations Ax = b unique?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"When A is square and nonsingular (det(A) ≠ 0)\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! A unique solution exists when A is a square matrix and has a non-zero determinant, meaning its rows or columns are linearly independent.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"When A is any rectangular matrix\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. Rectangular matrices (more equations than unknowns or vice versa) typically lead to either no solution or infinitely many solutions.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"When A is a singular matrix\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. A singular matrix (det(A) = 0) means the system either has no solution or infinitely many solutions.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"When A is a diagonal matrix\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. While diagonal matrices with non-zero diagonal elements do have unique solutions, this is not a necessary condition. Any nonsingular matrix will have a unique solution.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"Which of the following is the recommended way to solve a system of linear equations in Python?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"np.linalg.solve(A, b)\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! Using np.linalg.solve is the recommended approach as it's more numerically stable and computationally efficient than computing the inverse.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"np.linalg.inv(A) @ b\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. While this will work mathematically, it's not recommended because matrix inversion is numerically unstable and computationally expensive.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"A * b\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. This would perform element-wise multiplication, not solve the system of equations.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"A @ b\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. This performs matrix multiplication, not solving for x in Ax = b.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"What is the main advantage of LU decomposition for solving linear systems?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"It allows efficient solving for multiple right-hand sides after a single decomposition\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! Once the LU decomposition is computed, it can be reused to solve for different right-hand side vectors without repeating the most expensive part of the computation.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It always guarantees a solution even for singular matrices\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. LU decomposition does not guarantee solutions for singular matrices.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It's the only method that works with sparse matrices\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. There are several methods that work with sparse matrices, including iterative methods.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It requires less memory than other solution methods\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. While memory efficiency isn't the main advantage, the key benefit is computational efficiency when solving multiple systems with the same coefficient matrix.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"What makes a matrix 'sparse'?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"It contains many zero elements\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! A sparse matrix has many zero elements, allowing for specialized storage formats and algorithms that improve efficiency.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It has more columns than rows\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. A matrix with more columns than rows is called a wide matrix or has a rectangular shape, but this doesn't make it sparse.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"Its determinant is close to zero\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. A matrix with a determinant close to zero is called ill-conditioned, not sparse.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It contains only integer values\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. The type of values in a matrix doesn't determine whether it's sparse. Sparsity refers to the number of zero elements.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"Which of the following SciPy sparse matrix formats is most efficient for incrementally building a matrix?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"dok_matrix\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! dok_matrix (Dictionary of Keys) is based on a hash table and allows efficient random access, making it ideal for incrementally building a matrix.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"csr_matrix\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. While csr_matrix (Compressed Sparse Row) is efficient for arithmetic operations and row slicing, it's not the best choice for incrementally building a matrix.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"csc_matrix\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. csc_matrix (Compressed Sparse Column) is efficient for arithmetic operations and column slicing, but not for incrementally building a matrix.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"dia_matrix\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. dia_matrix is efficient for storing diagonal matrices but isn't designed for incrementally building general matrices.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"Which function in SciPy should be used to solve sparse linear systems?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"scipy.sparse.linalg.spsolve\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! spsolve is specifically designed to solve sparse linear systems efficiently.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"scipy.linalg.solve\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. scipy.linalg.solve is designed for dense matrices and would not take advantage of the sparsity structure.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"scipy.sparse.solve\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. This is not a valid function in SciPy. The correct function is scipy.sparse.linalg.spsolve.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"scipy.sparse.linalg.eigs\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. eigs computes eigenvalues and eigenvectors, not solutions to linear systems.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"Which sparse solver has shown superior performance for electrical circuit problems?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"KLU\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! As mentioned in the notebook, KLU (Kent Clark LU) shows superior performance for electrical circuit problems, including power systems.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"SuperLU\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. While SuperLU is a good general-purpose sparse solver, KLU is specifically optimized for circuit problems.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"UMFPACK\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. UMFPACK is another good general-purpose sparse solver, but it's not specifically optimized for electrical circuit problems like KLU.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"LAPACK\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. LAPACK is primarily for dense linear algebra, not sparse systems.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"Why is it not recommended to use matrix inversion (A⁻¹) to solve linear systems?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"It's numerically unstable and computationally expensive\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! Matrix inversion is both numerically less stable and computationally more expensive than direct solving methods like LU decomposition.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It only works for square matrices\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. While it's true that only square matrices can be inverted, this isn't the main reason to avoid using inversion for solving systems.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It's not supported in NumPy\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. NumPy does support matrix inversion with np.linalg.inv(), but it's still not recommended for solving systems.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"It only works for real-valued matrices\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. Matrix inversion works for complex-valued matrices as well, but is still not the recommended approach.\"\n",
- " }\n",
- " ]\n",
- " },\n",
- " {\n",
- " \"question\": \"What is the primary advantage of using a sparse matrix representation for power system admittance matrices?\",\n",
- " \"type\": \"multiple_choice\",\n",
- " \"answers\": [\n",
- " {\n",
- " \"answer\": \"Better efficiency and less memory usage\",\n",
- " \"correct\": true,\n",
- " \"feedback\": \"Correct! Power system admittance matrices are naturally sparse because buses are only connected to a few other buses. Using sparse representations provides better computational efficiency and significantly reduced memory usage.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"More accurate results in power flow calculations\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. Sparse matrix representation affects efficiency, not accuracy. The mathematical results should be identical to using dense matrices.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"Ability to handle complex numbers\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. Both sparse and dense matrices in Python can handle complex numbers for admittance matrices.\"\n",
- " },\n",
- " {\n",
- " \"answer\": \"Simplified matrix construction\",\n",
- " \"correct\": false,\n",
- " \"feedback\": \"Incorrect. Sparse matrices can actually be more complex to construct, but the efficiency benefits outweigh this complexity for large power systems.\"\n",
- " }\n",
- " ]\n",
- " }\n",
- "]\n",
- ";\n",
- " // Make a random ID\n",
- "function makeid(length) {\n",
- " var result = [];\n",
- " var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\n",
- " var charactersLength = characters.length;\n",
- " for (var i = 0; i < length; i++) {\n",
- " result.push(characters.charAt(Math.floor(Math.random() * charactersLength)));\n",
- " }\n",
- " return result.join('');\n",
- "}\n",
- "\n",
- "// Choose a random subset of an array. Can also be used to shuffle the array\n",
- "function getRandomSubarray(arr, size) {\n",
- " var shuffled = arr.slice(0), i = arr.length, temp, index;\n",
- " while (i--) {\n",
- " index = Math.floor((i + 1) * Math.random());\n",
- " temp = shuffled[index];\n",
- " shuffled[index] = shuffled[i];\n",
- " shuffled[i] = temp;\n",
- " }\n",
- " return shuffled.slice(0, size);\n",
- "}\n",
- "\n",
- "function printResponses(responsesContainer) {\n",
- " var responses=JSON.parse(responsesContainer.dataset.responses);\n",
- " var stringResponses='IMPORTANT!To preserve this answer sequence for submission, when you have finalized your answers: - Copy the text in this cell below \"Answer String\"
- Double click on the cell directly below the Answer String, labeled \"Replace Me\"
- Select the whole \"Replace Me\" text
- Paste in your answer string and press shift-Enter.
- Save the notebook using the save icon or File->Save Notebook menu item
Answer String:
';\n",
- " console.log(responses);\n",
- " responses.forEach((response, index) => {\n",
- " if (response) {\n",
- " console.log(index + ': ' + response);\n",
- " stringResponses+= index + ': ' + response +\"
\";\n",
- " }\n",
- " });\n",
- " responsesContainer.innerHTML=stringResponses;\n",
- "}\n",
- "/* Callback function to determine whether a selected multiple-choice\n",
- " button corresponded to a correct answer and to provide feedback\n",
- " based on the answer */\n",
- "function check_mc() {\n",
- " var id = this.id.split('-')[0];\n",
- " //var response = this.id.split('-')[1];\n",
- " //console.log(response);\n",
- " //console.log(\"In check_mc(), id=\"+id);\n",
- " //console.log(event.srcElement.id) \n",
- " //console.log(event.srcElement.dataset.correct) \n",
- " //console.log(event.srcElement.dataset.feedback)\n",
- "\n",
- " var label = event.srcElement;\n",
- " //console.log(label, label.nodeName);\n",
- " var depth = 0;\n",
- " while ((label.nodeName != \"LABEL\") && (depth < 20)) {\n",
- " label = label.parentElement;\n",
- " console.log(depth, label);\n",
- " depth++;\n",
- " }\n",
- "\n",
- "\n",
- "\n",
- " var answers = label.parentElement.children;\n",
- " //console.log(answers);\n",
- "\n",
- " // Split behavior based on multiple choice vs many choice:\n",
- " var fb = document.getElementById(\"fb\" + id);\n",
- "\n",
- "\n",
- "\n",
- " /* Multiple choice (1 answer). Allow for 0 correct\n",
- " answers as an edge case */\n",
- " if (fb.dataset.numcorrect <= 1) {\n",
- " // What follows is for the saved responses stuff\n",
- " var outerContainer = fb.parentElement.parentElement;\n",
- " var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n",
- " if (responsesContainer) {\n",
- " //console.log(responsesContainer);\n",
- " var response = label.firstChild.innerText;\n",
- " if (label.querySelector(\".QuizCode\")){\n",
- " response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n",
- " }\n",
- " console.log(response);\n",
- " //console.log(document.getElementById(\"quizWrap\"+id));\n",
- " var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n",
- " console.log(\"Question \" + qnum);\n",
- " //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n",
- " var responses=JSON.parse(responsesContainer.dataset.responses);\n",
- " console.log(responses);\n",
- " responses[qnum]= response;\n",
- " responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n",
- " printResponses(responsesContainer);\n",
- " }\n",
- " // End code to preserve responses\n",
- "\n",
- " for (var i = 0; i < answers.length; i++) {\n",
- " var child = answers[i];\n",
- " //console.log(child);\n",
- " child.className = \"MCButton\";\n",
- " }\n",
- "\n",
- "\n",
- "\n",
- " if (label.dataset.correct == \"true\") {\n",
- " // console.log(\"Correct action\");\n",
- " if (\"feedback\" in label.dataset) {\n",
- " fb.innerHTML = jaxify(label.dataset.feedback);\n",
- " } else {\n",
- " fb.innerHTML = \"Correct!\";\n",
- " }\n",
- " label.classList.add(\"correctButton\");\n",
- "\n",
- " fb.className = \"Feedback\";\n",
- " fb.classList.add(\"correct\");\n",
- "\n",
- " } else {\n",
- " if (\"feedback\" in label.dataset) {\n",
- " fb.innerHTML = jaxify(label.dataset.feedback);\n",
- " } else {\n",
- " fb.innerHTML = \"Incorrect -- try again.\";\n",
- " }\n",
- " //console.log(\"Error action\");\n",
- " label.classList.add(\"incorrectButton\");\n",
- " fb.className = \"Feedback\";\n",
- " fb.classList.add(\"incorrect\");\n",
- " }\n",
- " }\n",
- " else { /* Many choice (more than 1 correct answer) */\n",
- " var reset = false;\n",
- " var feedback;\n",
- " if (label.dataset.correct == \"true\") {\n",
- " if (\"feedback\" in label.dataset) {\n",
- " feedback = jaxify(label.dataset.feedback);\n",
- " } else {\n",
- " feedback = \"Correct!\";\n",
- " }\n",
- " if (label.dataset.answered <= 0) {\n",
- " if (fb.dataset.answeredcorrect < 0) {\n",
- " fb.dataset.answeredcorrect = 1;\n",
- " reset = true;\n",
- " } else {\n",
- " fb.dataset.answeredcorrect++;\n",
- " }\n",
- " if (reset) {\n",
- " for (var i = 0; i < answers.length; i++) {\n",
- " var child = answers[i];\n",
- " child.className = \"MCButton\";\n",
- " child.dataset.answered = 0;\n",
- " }\n",
- " }\n",
- " label.classList.add(\"correctButton\");\n",
- " label.dataset.answered = 1;\n",
- " fb.className = \"Feedback\";\n",
- " fb.classList.add(\"correct\");\n",
- "\n",
- " }\n",
- " } else {\n",
- " if (\"feedback\" in label.dataset) {\n",
- " feedback = jaxify(label.dataset.feedback);\n",
- " } else {\n",
- " feedback = \"Incorrect -- try again.\";\n",
- " }\n",
- " if (fb.dataset.answeredcorrect > 0) {\n",
- " fb.dataset.answeredcorrect = -1;\n",
- " reset = true;\n",
- " } else {\n",
- " fb.dataset.answeredcorrect--;\n",
- " }\n",
- "\n",
- " if (reset) {\n",
- " for (var i = 0; i < answers.length; i++) {\n",
- " var child = answers[i];\n",
- " child.className = \"MCButton\";\n",
- " child.dataset.answered = 0;\n",
- " }\n",
- " }\n",
- " label.classList.add(\"incorrectButton\");\n",
- " fb.className = \"Feedback\";\n",
- " fb.classList.add(\"incorrect\");\n",
- " }\n",
- " // What follows is for the saved responses stuff\n",
- " var outerContainer = fb.parentElement.parentElement;\n",
- " var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n",
- " if (responsesContainer) {\n",
- " //console.log(responsesContainer);\n",
- " var response = label.firstChild.innerText;\n",
- " if (label.querySelector(\".QuizCode\")){\n",
- " response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n",
- " }\n",
- " console.log(response);\n",
- " //console.log(document.getElementById(\"quizWrap\"+id));\n",
- " var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n",
- " console.log(\"Question \" + qnum);\n",
- " //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n",
- " var responses=JSON.parse(responsesContainer.dataset.responses);\n",
- " if (label.dataset.correct == \"true\") {\n",
- " if (typeof(responses[qnum]) == \"object\"){\n",
- " if (!responses[qnum].includes(response))\n",
- " responses[qnum].push(response);\n",
- " } else{\n",
- " responses[qnum]= [ response ];\n",
- " }\n",
- " } else {\n",
- " responses[qnum]= response;\n",
- " }\n",
- " console.log(responses);\n",
- " responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n",
- " printResponses(responsesContainer);\n",
- " }\n",
- " // End save responses stuff\n",
- "\n",
- "\n",
- "\n",
- " var numcorrect = fb.dataset.numcorrect;\n",
- " var answeredcorrect = fb.dataset.answeredcorrect;\n",
- " if (answeredcorrect >= 0) {\n",
- " fb.innerHTML = feedback + \" [\" + answeredcorrect + \"/\" + numcorrect + \"]\";\n",
- " } else {\n",
- " fb.innerHTML = feedback + \" [\" + 0 + \"/\" + numcorrect + \"]\";\n",
- " }\n",
- "\n",
- "\n",
- " }\n",
- "\n",
- " if (typeof MathJax != 'undefined') {\n",
- " var version = MathJax.version;\n",
- " console.log('MathJax version', version);\n",
- " if (version[0] == \"2\") {\n",
- " MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n",
- " } else if (version[0] == \"3\") {\n",
- " MathJax.typeset([fb]);\n",
- " }\n",
- " } else {\n",
- " console.log('MathJax not detected');\n",
- " }\n",
- "\n",
- "}\n",
- "\n",
- "\n",
- "/* Function to produce the HTML buttons for a multiple choice/\n",
- " many choice question and to update the CSS tags based on\n",
- " the question type */\n",
- "function make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id) {\n",
- "\n",
- " var shuffled;\n",
- " if (shuffle_answers == \"True\") {\n",
- " //console.log(shuffle_answers+\" read as true\");\n",
- " shuffled = getRandomSubarray(qa.answers, qa.answers.length);\n",
- " } else {\n",
- " //console.log(shuffle_answers+\" read as false\");\n",
- " shuffled = qa.answers;\n",
- " }\n",
- "\n",
- "\n",
- " var num_correct = 0;\n",
- "\n",
- " shuffled.forEach((item, index, ans_array) => {\n",
- " //console.log(answer);\n",
- "\n",
- " // Make input element\n",
- " var inp = document.createElement(\"input\");\n",
- " inp.type = \"radio\";\n",
- " inp.id = \"quizo\" + id + index;\n",
- " inp.style = \"display:none;\";\n",
- " aDiv.append(inp);\n",
- "\n",
- " //Make label for input element\n",
- " var lab = document.createElement(\"label\");\n",
- " lab.className = \"MCButton\";\n",
- " lab.id = id + '-' + index;\n",
- " lab.onclick = check_mc;\n",
- " var aSpan = document.createElement('span');\n",
- " aSpan.classsName = \"\";\n",
- " //qDiv.id=\"quizQn\"+id+index;\n",
- " if (\"answer\" in item) {\n",
- " aSpan.innerHTML = jaxify(item.answer);\n",
- " //aSpan.innerHTML=item.answer;\n",
- " }\n",
- " lab.append(aSpan);\n",
- "\n",
- " // Create div for code inside question\n",
- " var codeSpan;\n",
- " if (\"code\" in item) {\n",
- " codeSpan = document.createElement('span');\n",
- " codeSpan.id = \"code\" + id + index;\n",
- " codeSpan.className = \"QuizCode\";\n",
- " var codePre = document.createElement('pre');\n",
- " codeSpan.append(codePre);\n",
- " var codeCode = document.createElement('code');\n",
- " codePre.append(codeCode);\n",
- " codeCode.innerHTML = item.code;\n",
- " lab.append(codeSpan);\n",
- " //console.log(codeSpan);\n",
- " }\n",
- "\n",
- " //lab.textContent=item.answer;\n",
- "\n",
- " // Set the data attributes for the answer\n",
- " lab.setAttribute('data-correct', item.correct);\n",
- " if (item.correct) {\n",
- " num_correct++;\n",
- " }\n",
- " if (\"feedback\" in item) {\n",
- " lab.setAttribute('data-feedback', item.feedback);\n",
- " }\n",
- " lab.setAttribute('data-answered', 0);\n",
- "\n",
- " aDiv.append(lab);\n",
- "\n",
- " });\n",
- "\n",
- " if (num_correct > 1) {\n",
- " outerqDiv.className = \"ManyChoiceQn\";\n",
- " } else {\n",
- " outerqDiv.className = \"MultipleChoiceQn\";\n",
- " }\n",
- "\n",
- " return num_correct;\n",
- "\n",
- "}\n",
- "function check_numeric(ths, event) {\n",
- "\n",
- " if (event.keyCode === 13) {\n",
- " ths.blur();\n",
- "\n",
- " var id = ths.id.split('-')[0];\n",
- "\n",
- " var submission = ths.value;\n",
- " if (submission.indexOf('/') != -1) {\n",
- " var sub_parts = submission.split('/');\n",
- " //console.log(sub_parts);\n",
- " submission = sub_parts[0] / sub_parts[1];\n",
- " }\n",
- " //console.log(\"Reader entered\", submission);\n",
- "\n",
- " if (\"precision\" in ths.dataset) {\n",
- " var precision = ths.dataset.precision;\n",
- " submission = Number(Number(submission).toPrecision(precision));\n",
- " }\n",
- "\n",
- "\n",
- " //console.log(\"In check_numeric(), id=\"+id);\n",
- " //console.log(event.srcElement.id) \n",
- " //console.log(event.srcElement.dataset.feedback)\n",
- "\n",
- " var fb = document.getElementById(\"fb\" + id);\n",
- " fb.style.display = \"none\";\n",
- " fb.innerHTML = \"Incorrect -- try again.\";\n",
- "\n",
- " var answers = JSON.parse(ths.dataset.answers);\n",
- " //console.log(answers);\n",
- "\n",
- " var defaultFB = \"Incorrect. Try again.\";\n",
- " var correct;\n",
- " var done = false;\n",
- " answers.every(answer => {\n",
- " //console.log(answer.type);\n",
- "\n",
- " correct = false;\n",
- " // if (answer.type==\"value\"){\n",
- " if ('value' in answer) {\n",
- " if (submission == answer.value) {\n",
- " if (\"feedback\" in answer) {\n",
- " fb.innerHTML = jaxify(answer.feedback);\n",
- " } else {\n",
- " fb.innerHTML = jaxify(\"Correct\");\n",
- " }\n",
- " correct = answer.correct;\n",
- " //console.log(answer.correct);\n",
- " done = true;\n",
- " }\n",
- " // } else if (answer.type==\"range\") {\n",
- " } else if ('range' in answer) {\n",
- " console.log(answer.range);\n",
- " console.log(submission, submission >=answer.range[0], submission < answer.range[1])\n",
- " if ((submission >= answer.range[0]) && (submission < answer.range[1])) {\n",
- " fb.innerHTML = jaxify(answer.feedback);\n",
- " correct = answer.correct;\n",
- " console.log(answer.correct);\n",
- " done = true;\n",
- " }\n",
- " } else if (answer.type == \"default\") {\n",
- " if (\"feedback\" in answer) {\n",
- " defaultFB = answer.feedback;\n",
- " } \n",
- " }\n",
- " if (done) {\n",
- " return false; // Break out of loop if this has been marked correct\n",
- " } else {\n",
- " return true; // Keep looking for case that includes this as a correct answer\n",
- " }\n",
- " });\n",
- " console.log(\"done:\", done);\n",
- "\n",
- " if ((!done) && (defaultFB != \"\")) {\n",
- " fb.innerHTML = jaxify(defaultFB);\n",
- " //console.log(\"Default feedback\", defaultFB);\n",
- " }\n",
- "\n",
- " fb.style.display = \"block\";\n",
- " if (correct) {\n",
- " ths.className = \"Input-text\";\n",
- " ths.classList.add(\"correctButton\");\n",
- " fb.className = \"Feedback\";\n",
- " fb.classList.add(\"correct\");\n",
- " } else {\n",
- " ths.className = \"Input-text\";\n",
- " ths.classList.add(\"incorrectButton\");\n",
- " fb.className = \"Feedback\";\n",
- " fb.classList.add(\"incorrect\");\n",
- " }\n",
- "\n",
- " // What follows is for the saved responses stuff\n",
- " var outerContainer = fb.parentElement.parentElement;\n",
- " var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n",
- " if (responsesContainer) {\n",
- " console.log(submission);\n",
- " var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n",
- " //console.log(\"Question \" + qnum);\n",
- " //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n",
- " var responses=JSON.parse(responsesContainer.dataset.responses);\n",
- " console.log(responses);\n",
- " if (submission == ths.value){\n",
- " responses[qnum]= submission;\n",
- " } else {\n",
- " responses[qnum]= ths.value + \"(\" + submission +\")\";\n",
- " }\n",
- " responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n",
- " printResponses(responsesContainer);\n",
- " }\n",
- " // End code to preserve responses\n",
- "\n",
- " if (typeof MathJax != 'undefined') {\n",
- " var version = MathJax.version;\n",
- " console.log('MathJax version', version);\n",
- " if (version[0] == \"2\") {\n",
- " MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n",
- " } else if (version[0] == \"3\") {\n",
- " MathJax.typeset([fb]);\n",
- " }\n",
- " } else {\n",
- " console.log('MathJax not detected');\n",
- " }\n",
- " return false;\n",
- " }\n",
- "\n",
- "}\n",
- "\n",
- "function isValid(el, charC) {\n",
- " //console.log(\"Input char: \", charC);\n",
- " if (charC == 46) {\n",
- " if (el.value.indexOf('.') === -1) {\n",
- " return true;\n",
- " } else if (el.value.indexOf('/') != -1) {\n",
- " var parts = el.value.split('/');\n",
- " if (parts[1].indexOf('.') === -1) {\n",
- " return true;\n",
- " }\n",
- " }\n",
- " else {\n",
- " return false;\n",
- " }\n",
- " } else if (charC == 47) {\n",
- " if (el.value.indexOf('/') === -1) {\n",
- " if ((el.value != \"\") && (el.value != \".\")) {\n",
- " return true;\n",
- " } else {\n",
- " return false;\n",
- " }\n",
- " } else {\n",
- " return false;\n",
- " }\n",
- " } else if (charC == 45) {\n",
- " var edex = el.value.indexOf('e');\n",
- " if (edex == -1) {\n",
- " edex = el.value.indexOf('E');\n",
- " }\n",
- "\n",
- " if (el.value == \"\") {\n",
- " return true;\n",
- " } else if (edex == (el.value.length - 1)) { // If just after e or E\n",
- " return true;\n",
- " } else {\n",
- " return false;\n",
- " }\n",
- " } else if (charC == 101) { // \"e\"\n",
- " if ((el.value.indexOf('e') === -1) && (el.value.indexOf('E') === -1) && (el.value.indexOf('/') == -1)) {\n",
- " // Prev symbol must be digit or decimal point:\n",
- " if (el.value.slice(-1).search(/\\d/) >= 0) {\n",
- " return true;\n",
- " } else if (el.value.slice(-1).search(/\\./) >= 0) {\n",
- " return true;\n",
- " } else {\n",
- " return false;\n",
- " }\n",
- " } else {\n",
- " return false;\n",
- " }\n",
- " } else {\n",
- " if (charC > 31 && (charC < 48 || charC > 57))\n",
- " return false;\n",
- " }\n",
- " return true;\n",
- "}\n",
- "\n",
- "function numeric_keypress(evnt) {\n",
- " var charC = (evnt.which) ? evnt.which : evnt.keyCode;\n",
- "\n",
- " if (charC == 13) {\n",
- " check_numeric(this, evnt);\n",
- " } else {\n",
- " return isValid(this, charC);\n",
- " }\n",
- "}\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "function make_numeric(qa, outerqDiv, qDiv, aDiv, id) {\n",
- "\n",
- "\n",
- "\n",
- " //console.log(answer);\n",
- "\n",
- "\n",
- " outerqDiv.className = \"NumericQn\";\n",
- " aDiv.style.display = 'block';\n",
- "\n",
- " var lab = document.createElement(\"label\");\n",
- " lab.className = \"InpLabel\";\n",
- " lab.innerHTML = \"Type numeric answer here:\";\n",
- " aDiv.append(lab);\n",
- "\n",
- " var inp = document.createElement(\"input\");\n",
- " inp.type = \"text\";\n",
- " //inp.id=\"input-\"+id;\n",
- " inp.id = id + \"-0\";\n",
- " inp.className = \"Input-text\";\n",
- " inp.setAttribute('data-answers', JSON.stringify(qa.answers));\n",
- " if (\"precision\" in qa) {\n",
- " inp.setAttribute('data-precision', qa.precision);\n",
- " }\n",
- " aDiv.append(inp);\n",
- " //console.log(inp);\n",
- "\n",
- " //inp.addEventListener(\"keypress\", check_numeric);\n",
- " //inp.addEventListener(\"keypress\", numeric_keypress);\n",
- " /*\n",
- " inp.addEventListener(\"keypress\", function(event) {\n",
- " return numeric_keypress(this, event);\n",
- " }\n",
- " );\n",
- " */\n",
- " //inp.onkeypress=\"return numeric_keypress(this, event)\";\n",
- " inp.onkeypress = numeric_keypress;\n",
- " inp.onpaste = event => false;\n",
- "\n",
- " inp.addEventListener(\"focus\", function (event) {\n",
- " this.value = \"\";\n",
- " return false;\n",
- " }\n",
- " );\n",
- "\n",
- "\n",
- "}\n",
- "function jaxify(string) {\n",
- " var mystring = string;\n",
- "\n",
- " var count = 0;\n",
- " var loc = mystring.search(/([^\\\\]|^)(\\$)/);\n",
- "\n",
- " var count2 = 0;\n",
- " var loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n",
- "\n",
- " //console.log(loc);\n",
- "\n",
- " while ((loc >= 0) || (loc2 >= 0)) {\n",
- "\n",
- " /* Have to replace all the double $$ first with current implementation */\n",
- " if (loc2 >= 0) {\n",
- " if (count2 % 2 == 0) {\n",
- " mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\[\");\n",
- " } else {\n",
- " mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\]\");\n",
- " }\n",
- " count2++;\n",
- " } else {\n",
- " if (count % 2 == 0) {\n",
- " mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\(\");\n",
- " } else {\n",
- " mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\)\");\n",
- " }\n",
- " count++;\n",
- " }\n",
- " loc = mystring.search(/([^\\\\]|^)(\\$)/);\n",
- " loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n",
- " //console.log(mystring,\", loc:\",loc,\", loc2:\",loc2);\n",
- " }\n",
- "\n",
- " // repace markdown style links with actual links\n",
- " mystring = mystring.replace(/<(.*?)>/, '$1');\n",
- " mystring = mystring.replace(/\\[(.*?)\\]\\((.*?)\\)/, '$1');\n",
- "\n",
- " //console.log(mystring);\n",
- " return mystring;\n",
- "}\n",
- "\n",
- "\n",
- "function show_questions(json, mydiv) {\n",
- " console.log('show_questions');\n",
- " //var mydiv=document.getElementById(myid);\n",
- " var shuffle_questions = mydiv.dataset.shufflequestions;\n",
- " var num_questions = mydiv.dataset.numquestions;\n",
- " var shuffle_answers = mydiv.dataset.shuffleanswers;\n",
- " var max_width = mydiv.dataset.maxwidth;\n",
- "\n",
- " if (num_questions > json.length) {\n",
- " num_questions = json.length;\n",
- " }\n",
- "\n",
- " var questions;\n",
- " if ((num_questions < json.length) || (shuffle_questions == \"True\")) {\n",
- " //console.log(num_questions+\",\"+json.length);\n",
- " questions = getRandomSubarray(json, num_questions);\n",
- " } else {\n",
- " questions = json;\n",
- " }\n",
- "\n",
- " //console.log(\"SQ: \"+shuffle_questions+\", NQ: \" + num_questions + \", SA: \", shuffle_answers);\n",
- "\n",
- " // Iterate over questions\n",
- " questions.forEach((qa, index, array) => {\n",
- " //console.log(qa.question); \n",
- "\n",
- " var id = makeid(8);\n",
- " //console.log(id);\n",
- "\n",
- "\n",
- " // Create Div to contain question and answers\n",
- " var iDiv = document.createElement('div');\n",
- " //iDiv.id = 'quizWrap' + id + index;\n",
- " iDiv.id = 'quizWrap' + id;\n",
- " iDiv.className = 'Quiz';\n",
- " iDiv.setAttribute('data-qnum', index);\n",
- " iDiv.style.maxWidth =max_width+\"px\";\n",
- " mydiv.appendChild(iDiv);\n",
- " // iDiv.innerHTML=qa.question;\n",
- " \n",
- " var outerqDiv = document.createElement('div');\n",
- " outerqDiv.id = \"OuterquizQn\" + id + index;\n",
- " // Create div to contain question part\n",
- " var qDiv = document.createElement('div');\n",
- " qDiv.id = \"quizQn\" + id + index;\n",
- " \n",
- " if (qa.question) {\n",
- " iDiv.append(outerqDiv);\n",
- "\n",
- " //qDiv.textContent=qa.question;\n",
- " qDiv.innerHTML = jaxify(qa.question);\n",
- " outerqDiv.append(qDiv);\n",
- " }\n",
- "\n",
- " // Create div for code inside question\n",
- " var codeDiv;\n",
- " if (\"code\" in qa) {\n",
- " codeDiv = document.createElement('div');\n",
- " codeDiv.id = \"code\" + id + index;\n",
- " codeDiv.className = \"QuizCode\";\n",
- " var codePre = document.createElement('pre');\n",
- " codeDiv.append(codePre);\n",
- " var codeCode = document.createElement('code');\n",
- " codePre.append(codeCode);\n",
- " codeCode.innerHTML = qa.code;\n",
- " outerqDiv.append(codeDiv);\n",
- " //console.log(codeDiv);\n",
- " }\n",
- "\n",
- "\n",
- " // Create div to contain answer part\n",
- " var aDiv = document.createElement('div');\n",
- " aDiv.id = \"quizAns\" + id + index;\n",
- " aDiv.className = 'Answer';\n",
- " iDiv.append(aDiv);\n",
- "\n",
- " //console.log(qa.type);\n",
- "\n",
- " var num_correct;\n",
- " if ((qa.type == \"multiple_choice\") || (qa.type == \"many_choice\") ) {\n",
- " num_correct = make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id);\n",
- " if (\"answer_cols\" in qa) {\n",
- " //aDiv.style.gridTemplateColumns = 'auto '.repeat(qa.answer_cols);\n",
- " aDiv.style.gridTemplateColumns = 'repeat(' + qa.answer_cols + ', 1fr)';\n",
- " }\n",
- " } else if (qa.type == \"numeric\") {\n",
- " //console.log(\"numeric\");\n",
- " make_numeric(qa, outerqDiv, qDiv, aDiv, id);\n",
- " }\n",
- "\n",
- "\n",
- " //Make div for feedback\n",
- " var fb = document.createElement(\"div\");\n",
- " fb.id = \"fb\" + id;\n",
- " //fb.style=\"font-size: 20px;text-align:center;\";\n",
- " fb.className = \"Feedback\";\n",
- " fb.setAttribute(\"data-answeredcorrect\", 0);\n",
- " fb.setAttribute(\"data-numcorrect\", num_correct);\n",
- " iDiv.append(fb);\n",
- "\n",
- "\n",
- " });\n",
- " var preserveResponses = mydiv.dataset.preserveresponses;\n",
- " console.log(preserveResponses);\n",
- " console.log(preserveResponses == \"true\");\n",
- " if (preserveResponses == \"true\") {\n",
- " console.log(preserveResponses);\n",
- " // Create Div to contain record of answers\n",
- " var iDiv = document.createElement('div');\n",
- " iDiv.id = 'responses' + mydiv.id;\n",
- " iDiv.className = 'JCResponses';\n",
- " // Create a place to store responses as an empty array\n",
- " iDiv.setAttribute('data-responses', '[]');\n",
- "\n",
- " // Dummy Text\n",
- " iDiv.innerHTML=\"Select your answers and then follow the directions that will appear here.\"\n",
- " //iDiv.className = 'Quiz';\n",
- " mydiv.appendChild(iDiv);\n",
- " }\n",
- "//console.log(\"At end of show_questions\");\n",
- " if (typeof MathJax != 'undefined') {\n",
- " console.log(\"MathJax version\", MathJax.version);\n",
- " var version = MathJax.version;\n",
- " setTimeout(function(){\n",
- " var version = MathJax.version;\n",
- " console.log('After sleep, MathJax version', version);\n",
- " if (version[0] == \"2\") {\n",
- " MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n",
- " } else if (version[0] == \"3\") {\n",
- " if (MathJax.hasOwnProperty('typeset') ) {\n",
- " MathJax.typeset([mydiv]);\n",
- " } else {\n",
- " console.log('WARNING: Trying to force load MathJax 3');\n",
- " window.MathJax = {\n",
- " tex: {\n",
- " inlineMath: [['$', '$'], ['\\\\(', '\\\\)']]\n",
- " },\n",
- " svg: {\n",
- " fontCache: 'global'\n",
- " }\n",
- " };\n",
- "\n",
- " (function () {\n",
- " var script = document.createElement('script');\n",
- " script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js';\n",
- " script.async = true;\n",
- " document.head.appendChild(script);\n",
- " })();\n",
- " }\n",
- " }\n",
- " }, 500);\n",
- "if (typeof version == 'undefined') {\n",
- " } else\n",
- " {\n",
- " if (version[0] == \"2\") {\n",
- " MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n",
- " } else if (version[0] == \"3\") {\n",
- " if (MathJax.hasOwnProperty('typeset') ) {\n",
- " MathJax.typeset([mydiv]);\n",
- " } else {\n",
- " console.log('WARNING: Trying to force load MathJax 3');\n",
- " window.MathJax = {\n",
- " tex: {\n",
- " inlineMath: [['$', '$'], ['\\\\(', '\\\\)']]\n",
- " },\n",
- " svg: {\n",
- " fontCache: 'global'\n",
- " }\n",
- " };\n",
- "\n",
- " (function () {\n",
- " var script = document.createElement('script');\n",
- " script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js';\n",
- " script.async = true;\n",
- " document.head.appendChild(script);\n",
- " })();\n",
- " }\n",
- " } else {\n",
- " console.log(\"MathJax not found\");\n",
- " }\n",
- " }\n",
- " }\n",
- "\n",
- " // stop event propagation for the .Link class\n",
- " var links = document.getElementsByClassName('Link')\n",
- " for (var i = 0; i < links.length; i++) {\n",
- " links[i].addEventListener('click', function(e){\n",
- " e.stopPropagation();\n",
- " });\n",
- " }\n",
- "\n",
- " return false;\n",
- "}\n",
- "/* This is to handle asynchrony issues in loading Jupyter notebooks\n",
- " where the quiz has been previously run. The Javascript was generally\n",
- " being run before the div was added to the DOM. I tried to do this\n",
- " more elegantly using Mutation Observer, but I didn't get it to work.\n",
- "\n",
- " Someone more knowledgeable could make this better ;-) */\n",
- "\n",
- " function try_show() {\n",
- " if(document.getElementById(\"ENKrJXTGxdXc\")) {\n",
- " show_questions(questionsENKrJXTGxdXc, ENKrJXTGxdXc); \n",
- " } else {\n",
- " setTimeout(try_show, 200);\n",
- " }\n",
- " };\n",
- " \n",
- " //console.log(element);\n",
- " {\n",
- " const jmscontroller = new AbortController();\n",
- " const signal = jmscontroller.signal;\n",
- "\n",
- " setTimeout(() => jmscontroller.abort(), 5000);\n",
- "\n",
- " fetch(\"https://raw.githubusercontent.com/PowerCyberTraining/powercybertraining.github.io/refs/heads/cui/pct/modules/04/questions/linear-equations.json\", {signal})\n",
- " .then(response => response.json())\n",
- " .then(json => show_questions(json, ENKrJXTGxdXc))\n",
- " .catch(err => {\n",
- " console.log(\"Fetch error or timeout\");\n",
- " show_questions(questionsENKrJXTGxdXc, ENKrJXTGxdXc);\n",
- " });\n",
- " }\n",
- " "
- ],
+ "application/javascript": "var questionsENKrJXTGxdXc=[\n {\n \"question\": \"What is the standard form for a system of linear equations?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"Ax = b\",\n \"correct\": true,\n \"feedback\": \"Correct! A system of linear equations is written in standard form as Ax = b, where A is the coefficient matrix, x is the vector of unknowns, and b is the right-hand side vector.\"\n },\n {\n \"answer\": \"x = Ab\",\n \"correct\": false,\n \"feedback\": \"Incorrect. The standard form is Ax = b, not x = Ab.\"\n },\n {\n \"answer\": \"x = A^(-1)b\",\n \"correct\": false,\n \"feedback\": \"Incorrect. This is the solution to a system of linear equations (x = A^(-1)b), not the standard form of the system itself.\"\n },\n {\n \"answer\": \"b = xA\",\n \"correct\": false,\n \"feedback\": \"Incorrect. The standard form of a system of linear equations is Ax = b, not b = xA.\"\n }\n ]\n },\n {\n \"question\": \"When is the solution to a system of linear equations Ax = b unique?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"When A is square and nonsingular (det(A) ≠ 0)\",\n \"correct\": true,\n \"feedback\": \"Correct! A unique solution exists when A is a square matrix and has a non-zero determinant, meaning its rows or columns are linearly independent.\"\n },\n {\n \"answer\": \"When A is any rectangular matrix\",\n \"correct\": false,\n \"feedback\": \"Incorrect. Rectangular matrices (more equations than unknowns or vice versa) typically lead to either no solution or infinitely many solutions.\"\n },\n {\n \"answer\": \"When A is a singular matrix\",\n \"correct\": false,\n \"feedback\": \"Incorrect. A singular matrix (det(A) = 0) means the system either has no solution or infinitely many solutions.\"\n },\n {\n \"answer\": \"When A is a diagonal matrix\",\n \"correct\": false,\n \"feedback\": \"Incorrect. While diagonal matrices with non-zero diagonal elements do have unique solutions, this is not a necessary condition. Any nonsingular matrix will have a unique solution.\"\n }\n ]\n },\n {\n \"question\": \"Which of the following is the recommended way to solve a system of linear equations in Python?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"np.linalg.solve(A, b)\",\n \"correct\": true,\n \"feedback\": \"Correct! Using np.linalg.solve is the recommended approach as it's more numerically stable and computationally efficient than computing the inverse.\"\n },\n {\n \"answer\": \"np.linalg.inv(A) @ b\",\n \"correct\": false,\n \"feedback\": \"Incorrect. While this will work mathematically, it's not recommended because matrix inversion is numerically unstable and computationally expensive.\"\n },\n {\n \"answer\": \"A * b\",\n \"correct\": false,\n \"feedback\": \"Incorrect. This would perform element-wise multiplication, not solve the system of equations.\"\n },\n {\n \"answer\": \"A @ b\",\n \"correct\": false,\n \"feedback\": \"Incorrect. This performs matrix multiplication, not solving for x in Ax = b.\"\n }\n ]\n },\n {\n \"question\": \"What is the main advantage of LU decomposition for solving linear systems?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"It allows efficient solving for multiple right-hand sides after a single decomposition\",\n \"correct\": true,\n \"feedback\": \"Correct! Once the LU decomposition is computed, it can be reused to solve for different right-hand side vectors without repeating the most expensive part of the computation.\"\n },\n {\n \"answer\": \"It always guarantees a solution even for singular matrices\",\n \"correct\": false,\n \"feedback\": \"Incorrect. LU decomposition does not guarantee solutions for singular matrices.\"\n },\n {\n \"answer\": \"It's the only method that works with sparse matrices\",\n \"correct\": false,\n \"feedback\": \"Incorrect. There are several methods that work with sparse matrices, including iterative methods.\"\n },\n {\n \"answer\": \"It requires less memory than other solution methods\",\n \"correct\": false,\n \"feedback\": \"Incorrect. While memory efficiency isn't the main advantage, the key benefit is computational efficiency when solving multiple systems with the same coefficient matrix.\"\n }\n ]\n },\n {\n \"question\": \"What makes a matrix 'sparse'?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"It contains many zero elements\",\n \"correct\": true,\n \"feedback\": \"Correct! A sparse matrix has many zero elements, allowing for specialized storage formats and algorithms that improve efficiency.\"\n },\n {\n \"answer\": \"It has more columns than rows\",\n \"correct\": false,\n \"feedback\": \"Incorrect. A matrix with more columns than rows is called a wide matrix or has a rectangular shape, but this doesn't make it sparse.\"\n },\n {\n \"answer\": \"Its determinant is close to zero\",\n \"correct\": false,\n \"feedback\": \"Incorrect. A matrix with a determinant close to zero is called ill-conditioned, not sparse.\"\n },\n {\n \"answer\": \"It contains only integer values\",\n \"correct\": false,\n \"feedback\": \"Incorrect. The type of values in a matrix doesn't determine whether it's sparse. Sparsity refers to the number of zero elements.\"\n }\n ]\n },\n {\n \"question\": \"Which of the following SciPy sparse matrix formats is most efficient for incrementally building a matrix?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"dok_matrix\",\n \"correct\": true,\n \"feedback\": \"Correct! dok_matrix (Dictionary of Keys) is based on a hash table and allows efficient random access, making it ideal for incrementally building a matrix.\"\n },\n {\n \"answer\": \"csr_matrix\",\n \"correct\": false,\n \"feedback\": \"Incorrect. While csr_matrix (Compressed Sparse Row) is efficient for arithmetic operations and row slicing, it's not the best choice for incrementally building a matrix.\"\n },\n {\n \"answer\": \"csc_matrix\",\n \"correct\": false,\n \"feedback\": \"Incorrect. csc_matrix (Compressed Sparse Column) is efficient for arithmetic operations and column slicing, but not for incrementally building a matrix.\"\n },\n {\n \"answer\": \"dia_matrix\",\n \"correct\": false,\n \"feedback\": \"Incorrect. dia_matrix is efficient for storing diagonal matrices but isn't designed for incrementally building general matrices.\"\n }\n ]\n },\n {\n \"question\": \"Which function in SciPy should be used to solve sparse linear systems?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"scipy.sparse.linalg.spsolve\",\n \"correct\": true,\n \"feedback\": \"Correct! spsolve is specifically designed to solve sparse linear systems efficiently.\"\n },\n {\n \"answer\": \"scipy.linalg.solve\",\n \"correct\": false,\n \"feedback\": \"Incorrect. scipy.linalg.solve is designed for dense matrices and would not take advantage of the sparsity structure.\"\n },\n {\n \"answer\": \"scipy.sparse.solve\",\n \"correct\": false,\n \"feedback\": \"Incorrect. This is not a valid function in SciPy. The correct function is scipy.sparse.linalg.spsolve.\"\n },\n {\n \"answer\": \"scipy.sparse.linalg.eigs\",\n \"correct\": false,\n \"feedback\": \"Incorrect. eigs computes eigenvalues and eigenvectors, not solutions to linear systems.\"\n }\n ]\n },\n {\n \"question\": \"Which sparse solver has shown superior performance for electrical circuit problems?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"KLU\",\n \"correct\": true,\n \"feedback\": \"Correct! As mentioned in the notebook, KLU (Kent Clark LU) shows superior performance for electrical circuit problems, including power systems.\"\n },\n {\n \"answer\": \"SuperLU\",\n \"correct\": false,\n \"feedback\": \"Incorrect. While SuperLU is a good general-purpose sparse solver, KLU is specifically optimized for circuit problems.\"\n },\n {\n \"answer\": \"UMFPACK\",\n \"correct\": false,\n \"feedback\": \"Incorrect. UMFPACK is another good general-purpose sparse solver, but it's not specifically optimized for electrical circuit problems like KLU.\"\n },\n {\n \"answer\": \"LAPACK\",\n \"correct\": false,\n \"feedback\": \"Incorrect. LAPACK is primarily for dense linear algebra, not sparse systems.\"\n }\n ]\n },\n {\n \"question\": \"Why is it not recommended to use matrix inversion (A⁻¹) to solve linear systems?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"It's numerically unstable and computationally expensive\",\n \"correct\": true,\n \"feedback\": \"Correct! Matrix inversion is both numerically less stable and computationally more expensive than direct solving methods like LU decomposition.\"\n },\n {\n \"answer\": \"It only works for square matrices\",\n \"correct\": false,\n \"feedback\": \"Incorrect. While it's true that only square matrices can be inverted, this isn't the main reason to avoid using inversion for solving systems.\"\n },\n {\n \"answer\": \"It's not supported in NumPy\",\n \"correct\": false,\n \"feedback\": \"Incorrect. NumPy does support matrix inversion with np.linalg.inv(), but it's still not recommended for solving systems.\"\n },\n {\n \"answer\": \"It only works for real-valued matrices\",\n \"correct\": false,\n \"feedback\": \"Incorrect. Matrix inversion works for complex-valued matrices as well, but is still not the recommended approach.\"\n }\n ]\n },\n {\n \"question\": \"What is the primary advantage of using a sparse matrix representation for power system admittance matrices?\",\n \"type\": \"multiple_choice\",\n \"answers\": [\n {\n \"answer\": \"Better efficiency and less memory usage\",\n \"correct\": true,\n \"feedback\": \"Correct! Power system admittance matrices are naturally sparse because buses are only connected to a few other buses. Using sparse representations provides better computational efficiency and significantly reduced memory usage.\"\n },\n {\n \"answer\": \"More accurate results in power flow calculations\",\n \"correct\": false,\n \"feedback\": \"Incorrect. Sparse matrix representation affects efficiency, not accuracy. The mathematical results should be identical to using dense matrices.\"\n },\n {\n \"answer\": \"Ability to handle complex numbers\",\n \"correct\": false,\n \"feedback\": \"Incorrect. Both sparse and dense matrices in Python can handle complex numbers for admittance matrices.\"\n },\n {\n \"answer\": \"Simplified matrix construction\",\n \"correct\": false,\n \"feedback\": \"Incorrect. Sparse matrices can actually be more complex to construct, but the efficiency benefits outweigh this complexity for large power systems.\"\n }\n ]\n }\n]\n;\n // Make a random ID\nfunction makeid(length) {\n var result = [];\n var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\n var charactersLength = characters.length;\n for (var i = 0; i < length; i++) {\n result.push(characters.charAt(Math.floor(Math.random() * charactersLength)));\n }\n return result.join('');\n}\n\n// Choose a random subset of an array. Can also be used to shuffle the array\nfunction getRandomSubarray(arr, size) {\n var shuffled = arr.slice(0), i = arr.length, temp, index;\n while (i--) {\n index = Math.floor((i + 1) * Math.random());\n temp = shuffled[index];\n shuffled[index] = shuffled[i];\n shuffled[i] = temp;\n }\n return shuffled.slice(0, size);\n}\n\nfunction printResponses(responsesContainer) {\n var responses=JSON.parse(responsesContainer.dataset.responses);\n var stringResponses='IMPORTANT!To preserve this answer sequence for submission, when you have finalized your answers: - Copy the text in this cell below \"Answer String\"
- Double click on the cell directly below the Answer String, labeled \"Replace Me\"
- Select the whole \"Replace Me\" text
- Paste in your answer string and press shift-Enter.
- Save the notebook using the save icon or File->Save Notebook menu item
Answer String:
';\n console.log(responses);\n responses.forEach((response, index) => {\n if (response) {\n console.log(index + ': ' + response);\n stringResponses+= index + ': ' + response +\"
\";\n }\n });\n responsesContainer.innerHTML=stringResponses;\n}\n/* Callback function to determine whether a selected multiple-choice\n button corresponded to a correct answer and to provide feedback\n based on the answer */\nfunction check_mc() {\n var id = this.id.split('-')[0];\n //var response = this.id.split('-')[1];\n //console.log(response);\n //console.log(\"In check_mc(), id=\"+id);\n //console.log(event.srcElement.id) \n //console.log(event.srcElement.dataset.correct) \n //console.log(event.srcElement.dataset.feedback)\n\n var label = event.srcElement;\n //console.log(label, label.nodeName);\n var depth = 0;\n while ((label.nodeName != \"LABEL\") && (depth < 20)) {\n label = label.parentElement;\n console.log(depth, label);\n depth++;\n }\n\n\n\n var answers = label.parentElement.children;\n //console.log(answers);\n\n // Split behavior based on multiple choice vs many choice:\n var fb = document.getElementById(\"fb\" + id);\n\n\n\n /* Multiple choice (1 answer). Allow for 0 correct\n answers as an edge case */\n if (fb.dataset.numcorrect <= 1) {\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n //console.log(responsesContainer);\n var response = label.firstChild.innerText;\n if (label.querySelector(\".QuizCode\")){\n response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n }\n console.log(response);\n //console.log(document.getElementById(\"quizWrap\"+id));\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n console.log(responses);\n responses[qnum]= response;\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End code to preserve responses\n\n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n //console.log(child);\n child.className = \"MCButton\";\n }\n\n\n\n if (label.dataset.correct == \"true\") {\n // console.log(\"Correct action\");\n if (\"feedback\" in label.dataset) {\n fb.innerHTML = jaxify(label.dataset.feedback);\n } else {\n fb.innerHTML = \"Correct!\";\n }\n label.classList.add(\"correctButton\");\n\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n\n } else {\n if (\"feedback\" in label.dataset) {\n fb.innerHTML = jaxify(label.dataset.feedback);\n } else {\n fb.innerHTML = \"Incorrect -- try again.\";\n }\n //console.log(\"Error action\");\n label.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n }\n else { /* Many choice (more than 1 correct answer) */\n var reset = false;\n var feedback;\n if (label.dataset.correct == \"true\") {\n if (\"feedback\" in label.dataset) {\n feedback = jaxify(label.dataset.feedback);\n } else {\n feedback = \"Correct!\";\n }\n if (label.dataset.answered <= 0) {\n if (fb.dataset.answeredcorrect < 0) {\n fb.dataset.answeredcorrect = 1;\n reset = true;\n } else {\n fb.dataset.answeredcorrect++;\n }\n if (reset) {\n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n child.className = \"MCButton\";\n child.dataset.answered = 0;\n }\n }\n label.classList.add(\"correctButton\");\n label.dataset.answered = 1;\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n\n }\n } else {\n if (\"feedback\" in label.dataset) {\n feedback = jaxify(label.dataset.feedback);\n } else {\n feedback = \"Incorrect -- try again.\";\n }\n if (fb.dataset.answeredcorrect > 0) {\n fb.dataset.answeredcorrect = -1;\n reset = true;\n } else {\n fb.dataset.answeredcorrect--;\n }\n\n if (reset) {\n for (var i = 0; i < answers.length; i++) {\n var child = answers[i];\n child.className = \"MCButton\";\n child.dataset.answered = 0;\n }\n }\n label.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n //console.log(responsesContainer);\n var response = label.firstChild.innerText;\n if (label.querySelector(\".QuizCode\")){\n response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n }\n console.log(response);\n //console.log(document.getElementById(\"quizWrap\"+id));\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n if (label.dataset.correct == \"true\") {\n if (typeof(responses[qnum]) == \"object\"){\n if (!responses[qnum].includes(response))\n responses[qnum].push(response);\n } else{\n responses[qnum]= [ response ];\n }\n } else {\n responses[qnum]= response;\n }\n console.log(responses);\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End save responses stuff\n\n\n\n var numcorrect = fb.dataset.numcorrect;\n var answeredcorrect = fb.dataset.answeredcorrect;\n if (answeredcorrect >= 0) {\n fb.innerHTML = feedback + \" [\" + answeredcorrect + \"/\" + numcorrect + \"]\";\n } else {\n fb.innerHTML = feedback + \" [\" + 0 + \"/\" + numcorrect + \"]\";\n }\n\n\n }\n\n if (typeof MathJax != 'undefined') {\n var version = MathJax.version;\n console.log('MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([fb]);\n }\n } else {\n console.log('MathJax not detected');\n }\n\n}\n\n\n/* Function to produce the HTML buttons for a multiple choice/\n many choice question and to update the CSS tags based on\n the question type */\nfunction make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id) {\n\n var shuffled;\n if (shuffle_answers == \"True\") {\n //console.log(shuffle_answers+\" read as true\");\n shuffled = getRandomSubarray(qa.answers, qa.answers.length);\n } else {\n //console.log(shuffle_answers+\" read as false\");\n shuffled = qa.answers;\n }\n\n\n var num_correct = 0;\n\n shuffled.forEach((item, index, ans_array) => {\n //console.log(answer);\n\n // Make input element\n var inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.id = \"quizo\" + id + index;\n inp.style = \"display:none;\";\n aDiv.append(inp);\n\n //Make label for input element\n var lab = document.createElement(\"label\");\n lab.className = \"MCButton\";\n lab.id = id + '-' + index;\n lab.onclick = check_mc;\n var aSpan = document.createElement('span');\n aSpan.classsName = \"\";\n //qDiv.id=\"quizQn\"+id+index;\n if (\"answer\" in item) {\n aSpan.innerHTML = jaxify(item.answer);\n //aSpan.innerHTML=item.answer;\n }\n lab.append(aSpan);\n\n // Create div for code inside question\n var codeSpan;\n if (\"code\" in item) {\n codeSpan = document.createElement('span');\n codeSpan.id = \"code\" + id + index;\n codeSpan.className = \"QuizCode\";\n var codePre = document.createElement('pre');\n codeSpan.append(codePre);\n var codeCode = document.createElement('code');\n codePre.append(codeCode);\n codeCode.innerHTML = item.code;\n lab.append(codeSpan);\n //console.log(codeSpan);\n }\n\n //lab.textContent=item.answer;\n\n // Set the data attributes for the answer\n lab.setAttribute('data-correct', item.correct);\n if (item.correct) {\n num_correct++;\n }\n if (\"feedback\" in item) {\n lab.setAttribute('data-feedback', item.feedback);\n }\n lab.setAttribute('data-answered', 0);\n\n aDiv.append(lab);\n\n });\n\n if (num_correct > 1) {\n outerqDiv.className = \"ManyChoiceQn\";\n } else {\n outerqDiv.className = \"MultipleChoiceQn\";\n }\n\n return num_correct;\n\n}\nfunction check_numeric(ths, event) {\n\n if (event.keyCode === 13) {\n ths.blur();\n\n var id = ths.id.split('-')[0];\n\n var submission = ths.value;\n if (submission.indexOf('/') != -1) {\n var sub_parts = submission.split('/');\n //console.log(sub_parts);\n submission = sub_parts[0] / sub_parts[1];\n }\n //console.log(\"Reader entered\", submission);\n\n if (\"precision\" in ths.dataset) {\n var precision = ths.dataset.precision;\n submission = Number(Number(submission).toPrecision(precision));\n }\n\n\n //console.log(\"In check_numeric(), id=\"+id);\n //console.log(event.srcElement.id) \n //console.log(event.srcElement.dataset.feedback)\n\n var fb = document.getElementById(\"fb\" + id);\n fb.style.display = \"none\";\n fb.innerHTML = \"Incorrect -- try again.\";\n\n var answers = JSON.parse(ths.dataset.answers);\n //console.log(answers);\n\n var defaultFB = \"Incorrect. Try again.\";\n var correct;\n var done = false;\n answers.every(answer => {\n //console.log(answer.type);\n\n correct = false;\n // if (answer.type==\"value\"){\n if ('value' in answer) {\n if (submission == answer.value) {\n if (\"feedback\" in answer) {\n fb.innerHTML = jaxify(answer.feedback);\n } else {\n fb.innerHTML = jaxify(\"Correct\");\n }\n correct = answer.correct;\n //console.log(answer.correct);\n done = true;\n }\n // } else if (answer.type==\"range\") {\n } else if ('range' in answer) {\n console.log(answer.range);\n console.log(submission, submission >=answer.range[0], submission < answer.range[1])\n if ((submission >= answer.range[0]) && (submission < answer.range[1])) {\n fb.innerHTML = jaxify(answer.feedback);\n correct = answer.correct;\n console.log(answer.correct);\n done = true;\n }\n } else if (answer.type == \"default\") {\n if (\"feedback\" in answer) {\n defaultFB = answer.feedback;\n } \n }\n if (done) {\n return false; // Break out of loop if this has been marked correct\n } else {\n return true; // Keep looking for case that includes this as a correct answer\n }\n });\n console.log(\"done:\", done);\n\n if ((!done) && (defaultFB != \"\")) {\n fb.innerHTML = jaxify(defaultFB);\n //console.log(\"Default feedback\", defaultFB);\n }\n\n fb.style.display = \"block\";\n if (correct) {\n ths.className = \"Input-text\";\n ths.classList.add(\"correctButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"correct\");\n } else {\n ths.className = \"Input-text\";\n ths.classList.add(\"incorrectButton\");\n fb.className = \"Feedback\";\n fb.classList.add(\"incorrect\");\n }\n\n // What follows is for the saved responses stuff\n var outerContainer = fb.parentElement.parentElement;\n var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n if (responsesContainer) {\n console.log(submission);\n var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n //console.log(\"Question \" + qnum);\n //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n var responses=JSON.parse(responsesContainer.dataset.responses);\n console.log(responses);\n if (submission == ths.value){\n responses[qnum]= submission;\n } else {\n responses[qnum]= ths.value + \"(\" + submission +\")\";\n }\n responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n printResponses(responsesContainer);\n }\n // End code to preserve responses\n\n if (typeof MathJax != 'undefined') {\n var version = MathJax.version;\n console.log('MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n MathJax.typeset([fb]);\n }\n } else {\n console.log('MathJax not detected');\n }\n return false;\n }\n\n}\n\nfunction isValid(el, charC) {\n //console.log(\"Input char: \", charC);\n if (charC == 46) {\n if (el.value.indexOf('.') === -1) {\n return true;\n } else if (el.value.indexOf('/') != -1) {\n var parts = el.value.split('/');\n if (parts[1].indexOf('.') === -1) {\n return true;\n }\n }\n else {\n return false;\n }\n } else if (charC == 47) {\n if (el.value.indexOf('/') === -1) {\n if ((el.value != \"\") && (el.value != \".\")) {\n return true;\n } else {\n return false;\n }\n } else {\n return false;\n }\n } else if (charC == 45) {\n var edex = el.value.indexOf('e');\n if (edex == -1) {\n edex = el.value.indexOf('E');\n }\n\n if (el.value == \"\") {\n return true;\n } else if (edex == (el.value.length - 1)) { // If just after e or E\n return true;\n } else {\n return false;\n }\n } else if (charC == 101) { // \"e\"\n if ((el.value.indexOf('e') === -1) && (el.value.indexOf('E') === -1) && (el.value.indexOf('/') == -1)) {\n // Prev symbol must be digit or decimal point:\n if (el.value.slice(-1).search(/\\d/) >= 0) {\n return true;\n } else if (el.value.slice(-1).search(/\\./) >= 0) {\n return true;\n } else {\n return false;\n }\n } else {\n return false;\n }\n } else {\n if (charC > 31 && (charC < 48 || charC > 57))\n return false;\n }\n return true;\n}\n\nfunction numeric_keypress(evnt) {\n var charC = (evnt.which) ? evnt.which : evnt.keyCode;\n\n if (charC == 13) {\n check_numeric(this, evnt);\n } else {\n return isValid(this, charC);\n }\n}\n\n\n\n\n\nfunction make_numeric(qa, outerqDiv, qDiv, aDiv, id) {\n\n\n\n //console.log(answer);\n\n\n outerqDiv.className = \"NumericQn\";\n aDiv.style.display = 'block';\n\n var lab = document.createElement(\"label\");\n lab.className = \"InpLabel\";\n lab.innerHTML = \"Type numeric answer here:\";\n aDiv.append(lab);\n\n var inp = document.createElement(\"input\");\n inp.type = \"text\";\n //inp.id=\"input-\"+id;\n inp.id = id + \"-0\";\n inp.className = \"Input-text\";\n inp.setAttribute('data-answers', JSON.stringify(qa.answers));\n if (\"precision\" in qa) {\n inp.setAttribute('data-precision', qa.precision);\n }\n aDiv.append(inp);\n //console.log(inp);\n\n //inp.addEventListener(\"keypress\", check_numeric);\n //inp.addEventListener(\"keypress\", numeric_keypress);\n /*\n inp.addEventListener(\"keypress\", function(event) {\n return numeric_keypress(this, event);\n }\n );\n */\n //inp.onkeypress=\"return numeric_keypress(this, event)\";\n inp.onkeypress = numeric_keypress;\n inp.onpaste = event => false;\n\n inp.addEventListener(\"focus\", function (event) {\n this.value = \"\";\n return false;\n }\n );\n\n\n}\nfunction jaxify(string) {\n var mystring = string;\n\n var count = 0;\n var loc = mystring.search(/([^\\\\]|^)(\\$)/);\n\n var count2 = 0;\n var loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n\n //console.log(loc);\n\n while ((loc >= 0) || (loc2 >= 0)) {\n\n /* Have to replace all the double $$ first with current implementation */\n if (loc2 >= 0) {\n if (count2 % 2 == 0) {\n mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\[\");\n } else {\n mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\]\");\n }\n count2++;\n } else {\n if (count % 2 == 0) {\n mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\(\");\n } else {\n mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\)\");\n }\n count++;\n }\n loc = mystring.search(/([^\\\\]|^)(\\$)/);\n loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n //console.log(mystring,\", loc:\",loc,\", loc2:\",loc2);\n }\n\n // repace markdown style links with actual links\n mystring = mystring.replace(/<(.*?)>/, '$1');\n mystring = mystring.replace(/\\[(.*?)\\]\\((.*?)\\)/, '$1');\n\n //console.log(mystring);\n return mystring;\n}\n\n\nfunction show_questions(json, mydiv) {\n console.log('show_questions');\n //var mydiv=document.getElementById(myid);\n var shuffle_questions = mydiv.dataset.shufflequestions;\n var num_questions = mydiv.dataset.numquestions;\n var shuffle_answers = mydiv.dataset.shuffleanswers;\n var max_width = mydiv.dataset.maxwidth;\n\n if (num_questions > json.length) {\n num_questions = json.length;\n }\n\n var questions;\n if ((num_questions < json.length) || (shuffle_questions == \"True\")) {\n //console.log(num_questions+\",\"+json.length);\n questions = getRandomSubarray(json, num_questions);\n } else {\n questions = json;\n }\n\n //console.log(\"SQ: \"+shuffle_questions+\", NQ: \" + num_questions + \", SA: \", shuffle_answers);\n\n // Iterate over questions\n questions.forEach((qa, index, array) => {\n //console.log(qa.question); \n\n var id = makeid(8);\n //console.log(id);\n\n\n // Create Div to contain question and answers\n var iDiv = document.createElement('div');\n //iDiv.id = 'quizWrap' + id + index;\n iDiv.id = 'quizWrap' + id;\n iDiv.className = 'Quiz';\n iDiv.setAttribute('data-qnum', index);\n iDiv.style.maxWidth =max_width+\"px\";\n mydiv.appendChild(iDiv);\n // iDiv.innerHTML=qa.question;\n \n var outerqDiv = document.createElement('div');\n outerqDiv.id = \"OuterquizQn\" + id + index;\n // Create div to contain question part\n var qDiv = document.createElement('div');\n qDiv.id = \"quizQn\" + id + index;\n \n if (qa.question) {\n iDiv.append(outerqDiv);\n\n //qDiv.textContent=qa.question;\n qDiv.innerHTML = jaxify(qa.question);\n outerqDiv.append(qDiv);\n }\n\n // Create div for code inside question\n var codeDiv;\n if (\"code\" in qa) {\n codeDiv = document.createElement('div');\n codeDiv.id = \"code\" + id + index;\n codeDiv.className = \"QuizCode\";\n var codePre = document.createElement('pre');\n codeDiv.append(codePre);\n var codeCode = document.createElement('code');\n codePre.append(codeCode);\n codeCode.innerHTML = qa.code;\n outerqDiv.append(codeDiv);\n //console.log(codeDiv);\n }\n\n\n // Create div to contain answer part\n var aDiv = document.createElement('div');\n aDiv.id = \"quizAns\" + id + index;\n aDiv.className = 'Answer';\n iDiv.append(aDiv);\n\n //console.log(qa.type);\n\n var num_correct;\n if ((qa.type == \"multiple_choice\") || (qa.type == \"many_choice\") ) {\n num_correct = make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id);\n if (\"answer_cols\" in qa) {\n //aDiv.style.gridTemplateColumns = 'auto '.repeat(qa.answer_cols);\n aDiv.style.gridTemplateColumns = 'repeat(' + qa.answer_cols + ', 1fr)';\n }\n } else if (qa.type == \"numeric\") {\n //console.log(\"numeric\");\n make_numeric(qa, outerqDiv, qDiv, aDiv, id);\n }\n\n\n //Make div for feedback\n var fb = document.createElement(\"div\");\n fb.id = \"fb\" + id;\n //fb.style=\"font-size: 20px;text-align:center;\";\n fb.className = \"Feedback\";\n fb.setAttribute(\"data-answeredcorrect\", 0);\n fb.setAttribute(\"data-numcorrect\", num_correct);\n iDiv.append(fb);\n\n\n });\n var preserveResponses = mydiv.dataset.preserveresponses;\n console.log(preserveResponses);\n console.log(preserveResponses == \"true\");\n if (preserveResponses == \"true\") {\n console.log(preserveResponses);\n // Create Div to contain record of answers\n var iDiv = document.createElement('div');\n iDiv.id = 'responses' + mydiv.id;\n iDiv.className = 'JCResponses';\n // Create a place to store responses as an empty array\n iDiv.setAttribute('data-responses', '[]');\n\n // Dummy Text\n iDiv.innerHTML=\"Select your answers and then follow the directions that will appear here.\"\n //iDiv.className = 'Quiz';\n mydiv.appendChild(iDiv);\n }\n//console.log(\"At end of show_questions\");\n if (typeof MathJax != 'undefined') {\n console.log(\"MathJax version\", MathJax.version);\n var version = MathJax.version;\n setTimeout(function(){\n var version = MathJax.version;\n console.log('After sleep, MathJax version', version);\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n if (MathJax.hasOwnProperty('typeset') ) {\n MathJax.typeset([mydiv]);\n } else {\n console.log('WARNING: Trying to force load MathJax 3');\n window.MathJax = {\n tex: {\n inlineMath: [['$', '$'], ['\\\\(', '\\\\)']]\n },\n svg: {\n fontCache: 'global'\n }\n };\n\n (function () {\n var script = document.createElement('script');\n script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js';\n script.async = true;\n document.head.appendChild(script);\n })();\n }\n }\n }, 500);\nif (typeof version == 'undefined') {\n } else\n {\n if (version[0] == \"2\") {\n MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n } else if (version[0] == \"3\") {\n if (MathJax.hasOwnProperty('typeset') ) {\n MathJax.typeset([mydiv]);\n } else {\n console.log('WARNING: Trying to force load MathJax 3');\n window.MathJax = {\n tex: {\n inlineMath: [['$', '$'], ['\\\\(', '\\\\)']]\n },\n svg: {\n fontCache: 'global'\n }\n };\n\n (function () {\n var script = document.createElement('script');\n script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js';\n script.async = true;\n document.head.appendChild(script);\n })();\n }\n } else {\n console.log(\"MathJax not found\");\n }\n }\n }\n\n // stop event propagation for the .Link class\n var links = document.getElementsByClassName('Link')\n for (var i = 0; i < links.length; i++) {\n links[i].addEventListener('click', function(e){\n e.stopPropagation();\n });\n }\n\n return false;\n}\n/* This is to handle asynchrony issues in loading Jupyter notebooks\n where the quiz has been previously run. The Javascript was generally\n being run before the div was added to the DOM. I tried to do this\n more elegantly using Mutation Observer, but I didn't get it to work.\n\n Someone more knowledgeable could make this better ;-) */\n\n function try_show() {\n if(document.getElementById(\"ENKrJXTGxdXc\")) {\n show_questions(questionsENKrJXTGxdXc, ENKrJXTGxdXc); \n } else {\n setTimeout(try_show, 200);\n }\n };\n \n //console.log(element);\n {\n const jmscontroller = new AbortController();\n const signal = jmscontroller.signal;\n\n setTimeout(() => jmscontroller.abort(), 5000);\n\n fetch(\"https://raw.githubusercontent.com/PowerCyberTraining/powercybertraining.github.io/refs/heads/cui/pct/modules/04/questions/linear-equations.json\", {signal})\n .then(response => response.json())\n .then(json => show_questions(json, ENKrJXTGxdXc))\n .catch(err => {\n console.log(\"Fetch error or timeout\");\n show_questions(questionsENKrJXTGxdXc, ENKrJXTGxdXc);\n });\n }\n ",
"text/plain": [
""
]
@@ -3685,7 +1506,7 @@
],
"metadata": {
"kernelspec": {
- "display_name": "Python 3 (ipykernel)",
+ "display_name": "a",
"language": "python",
"name": "python3"
},