Problem Statement

Obviously, an exercise needs to state the actual problem that has to be solved. This is the purpose of the problem() method, the only mandatory method of an exercise. Let’s first give a simple example with a single input field and a static answer.

class Multiplication(pyrope.Exercise):

    def problem(self):
        return pyrope.Problem(
            r'What is $6 \times 7$? <<answer>>',
            answer=pyrope.Natural()
        )

    def the_solution(self):
        return 42

The problem statement consists of a template with placeholders for input and output fields, together with a specification of the input field types. It is is created by calling pyrope.Problem() with appropriate parameters.

pyrope.Problem(template, **ifields)

Template

An exercise template is a string with placeholders for input and output fields. The string is interpreted as Markdown to allow for basic formatting such as

Template strings will usually span multiple lines and Python’s triple quoted strings are best suited for that. In order to allow for a consistent source code indentation, PyRope will eliminate any common leading whitespace from every line in the template string.

When the template is rendered, line breaks are ignored, since text is wrapped dynamically. To enforce a line break use a backslash at the end of a line. Note that the backslash has to be escaped as \\ unless you use raw strings.

An empty line in the template will insert a small vertical space and start a new paragraph.

For mathematical expressions, PyRope admits the use of \(\LaTeX\) if it is properly enclosed in $ respectively $$ delimiters.

Tip

Using raw strings for template strings containing \(\LaTeX\) will prevent Python from interpreting the backslashes in \(\LaTeX\) commands as escape sequences. For example, write r'... $\beta$ ...' to avoid that \b is understood as a backspace. Otherwise you would have to write '...$\\beta$...'.

Placeholders

The syntax for a placeholder is <<name>>, where name is the name of the input or output field. The delimiters << and >> were chosen to interfere as little as possible with markup, \(\LaTeX\), HTML or Python. Placeholders can be placed within most Markdown elements, but only output fields can be inserted into \(\LaTeX\) environments in the following manner <<name:latex>>. Note that there is no syntactical difference between placeholders for input and output fields. In the exercise below, for example, a and b are output fields whereas answer is an input field.

import random

class Multiplication(pyrope.Exercise):

    def parameters(self):
        return dict(
            a=random.randint(2, 9),
            b=random.randint(2, 9),
        )

    def problem(self, a, b):
        return pyrope.Problem(
            r'$<<a:latex>> \times <<b:latex>> =$ <<answer>>',
            answer=pyrope.Natural()
        )

    def the_solution(self, a, b):
        return a * b

Input Fields

Input fields in PyRope are typed. This assures two important facts:

  1. The learner gets immediate visual feedback on syntactically invalid input.

  2. The instructor is guaranteed that variables coming from user input have the correct type.

Input field

Python Type

Boolean or Bool

bool

Complex

complex

Dict

dict

Equation

sympy.Equality

Expression

sympy.Expr

Integer or Int

int

LinearExpression

sympy.Poly, degree one

List

list

Matrix

numpy.array, two-dimensional

MultipleChoice

dict

Natural

int, non-negative

OneOf

object

Polynomial

sympy.Poly

Rational

fractions.Fraction

Real

float

Set

set

String

str

Tuple

tuple

Vector

numpy.array, one-dimensional

The keyword arguments to pyrope.Problem() define which placeholders stand for input fields. The keys are the names of the input fields and the values are the input fields, created by calling the corresponding constructor.

Attention

Currently it is not possible to place input fields within \(\LaTeX\) environments, although this is planned for the future. For the time being, there are two options to deal with this:

  1. Break up the \(\LaTeX\) environment for the input fields.

  2. Use variables instead and ask for them in separate input fields outside the \(\LaTeX\) environment.

For example, you can not use r'... $\frac{<<a>>}{<<b>>}$ ...' to ask for a fraction with separated input fields for numerator \(a\) and denominator \(b\). Instead, use:

pyrope.Problem(
    r'... $\frac{a}{b}$ with $a=$ <<a>> and $b=$ <<b>> ...',
    a=pyrope.Int(),
    b=pyrope.Int()
)

Note that PyRope also provides a pyrope.Rational input field for fractions. With the elementwise=True option, numerator and denominator have separate input fields.

Output Fields

Any placeholder whose name is not a keyword argument to pyrope.Problem() is considered an output field and will be replaced by the corresponding parameter when the problem is rendered. Therefore any output parameter must be part of the parameters returned by the parameters() method. PyRope takes care to properly render output fields according to the corresponding parameter’s type. In this way you can, for instance, embed dynamically generated Pyplot figures into your problem statement.

Note that it is not necessary to specify the output parameters as keyword parameters to the problem() method if they only appear in the template string. Your linter or IDE will probably complain about unused variables if you do so.

Output fields can be placed in all Markdown elements, even within \(\LaTeX\) environments. However, you have to indicate when an output field name is placed inside a \(\LaTeX\) environment to the frontend by using the special syntax <<name:latex>> so that it can be rendered properly.

import random

class SquareRoot(pyrope.Exercise):

    def parameters(self):
        root = random.randint(1, 9)
        return dict(root=root, square=root**2)

    def problem(self):
        return pyrope.Problem(
            r'The square root $\sqrt{<<square:latex>>}$ equals <<root>>.',
            root=pyrope.Natural()
        )

    def the_solution(self, root):
        return root