This commit is contained in:
Chris Pyles 2021-02-23 21:49:57 -08:00
Родитель 40b1944a48
Коммит a44d063f90
9 изменённых файлов: 210 добавлений и 11 удалений

Просмотреть файл

@ -1,9 +1,16 @@
.. _api:
API Reference
=============
Annotations
-----------
.. autoclass:: pybryt.Annotation
:members:
:undoc-members:
.. autoclass:: pybryt.Value
:members:
:undoc-members:
@ -12,13 +19,15 @@ Annotations
:members:
:undoc-members:
Annotation Results
++++++++++++++++++
.. autoclass:: pybryt.annotations.annotation.AnnotationResult
.. autoclass:: pybryt.AnnotationResult
:members:
:undoc-members:
Reference Implementations
-------------------------
@ -26,6 +35,7 @@ Reference Implementations
:members:
:undoc-members:
Reference Results
+++++++++++++++++
@ -33,6 +43,7 @@ Reference Results
:members:
:undoc-members:
Student Implementations
-----------------------

Просмотреть файл

@ -82,7 +82,6 @@ html_theme = 'furo'
html_theme_options = {
# 'github_url': 'https://github.com/microsoft/pybryt',
# 'repository_url': 'https://github.com/microsoft/pybryt',
'navigation_depth': 3,
}
# github_url = 'https://github.com/microsoft'

Просмотреть файл

@ -6,6 +6,13 @@
PyBryt Documentation
====================
.. toctree::
:maxdepth: 3
:hidden:
reference_implementations/index
api_reference
PyBryt is an open source Python auto-assessment library for teaching and learning. Our goal is to empower students and educators to learn about technology through fun, guided, hands-on content aimed at specific learning goals. PyBryt is designed to work with existing autograding solutions and workflows such as `Otter Grader`_, `OkPy`_, and `Autolab`_.
.. image:: _static/images/pybryt_goals.png
@ -19,13 +26,6 @@ Educators and institutions can leverage PyBryt to integrate auto-assessment and
- Plagiarism detection and support for reference implementations
- Easy integration into existing organizational or institutional grading infrastructure
.. toctree::
:maxdepth: 3
:hidden:
reference_implementations
api_reference
.. _Otter Grader: https://otter-grader.readthedocs.io
.. _OkPy: https://okpy.org
.. _Autolab: https://autolab.readthedocs.io/

Просмотреть файл

@ -1,2 +0,0 @@
Reference Implementations
=========================

Просмотреть файл

@ -0,0 +1,154 @@
Annotations
===========
Annotations are the building blocks out of which reference implementations are constructed. They
annotations represent a single condition that a student's implementation should meet, and define a
set of behaviors for responding to the passing or failing of those conditions. Annotations provide
conditions not only for expecting a specific value but also for combining those expectations to form
conditions on the structure of students' code, including the temporal relationship of values and
complex boolean logic surrounding the presence, or lack thereof, of those values.
Values
------
All annotations are created by instaniating subclasses of the abstract :py:class:`pybryt.Annotation`
class. Consider the most basic kind of annotation: expecting a specific value to appear while
executing the student's code. To create a value annotation, create an instance of
:py:class:`pybryt.Value`. The constructor takes in the value that you are expecting to see in the
student's code:
.. code-block:: python
np.random.seed(42)
arr = np.random.normal(3, 2, size=100)
pybryt.Value(arr)
Note that when an instance of :py:class:`pybryt.Value` is created, :py:meth:`copy.copy` is called on
the argument passed to it, so values don't need to worry about being affected by mutability.
Numerical Tolerance
+++++++++++++++++++
For numerical values, or iterables of numerical values that support vectorized math, it is also
possible to set an absolute tolerance for the acceptance of student values using the ``tol`` argument,
which defaults to zero.
.. code-block:: python
pybryt.Value(arr, tol=1e-4)
Invariants
++++++++++
PyBryt supports :ref:`invariants<invariants>`, which are conditions that allow for objects with
different structures to be considered "equal" from PyBryt's perspective. To use invariants on a
value, pass the invariant objects in a list to the ``invariants`` argument of the constructor:
.. code-block:: python
correct_answer = "a CasE-inSensiTiVe stRING"
pybryt.Value(correct_answer, invariants=[pybryt.invariants.string_capitalization])
Relational Annotations
----------------------
Relational annotations define some kind of relationship between two or more annotations. Currently,
PyBryt supports two kinds of relational annotations: temporal annotations and boolean annotations.
All relational annotations are subclasses of the abstract
:py:class:`pybryt.BeforeAnnotation<pybryt.annotations.relation.BeforeAnnotation>` class, which
defines some helpful defaults for working with annotations that have child annotations.
Temporal Annotations
++++++++++++++++++++
Temporal annotations describe *when* variables should appear in students' code relative to one
another. For example, consider the problem of a dynamic programming algorithm to compute the
Fibonacci sequence: the array containing :math:`n-1` first Fibonacci numbers should appear in memory
before the array containing the :math:`n` first Fibonacci numbers.
To enforce such a constraint, the :py:class:`pybryt.Annotation` class defines a ``before`` method
that asserts that one annotation occurs before another:
.. code-block:: python
def fib(n):
"""
Compute and return an array of the first n Fibonacci numbers using dynamic programming.
Args:
n (``int``): the number of Fibonacci numbers to return
Returns:
``np.ndarray``: the first ``n`` Fibonacci numbers
"""
fibs = np.zeros(n, dtype=int)
fibs[0] = 0
curr_val = pybryt.Value(fibs)
if n == 1:
return fibs
fibs[1] = 1
v = pybryt.Value(fibs)
curr_val.before(v)
curr_val = v
if n == 2:
return fibs
for i in range(2, n-1):
fibs[i] = fibs[i-1] + fibs[i-2]
v = pybryt.Value(fibs) # array of first n Fibonacci numbrs
curr_val.before(v) # check that first n-1 Fib numbers come before first n
curr_val = v # update curr_val for next iteration
return fibs
In the example above, updating a pointer ``curr_val`` in the loop allows us to create a ``before``
condition such that we ensure the student followed the correct dynamic programming algorithm by
checking each update to the ``fibs`` array.
Temporal annotations are satisfied when the student's code satisfies all of the child
:py:class:`pybryt.Value` annotations and when the first annotation (the one calling
:py:meth:`pybryt.Annotation.before`) has a timestamp greater than or equal to the timestamp of the
second annotation.
Note that :py:meth:`pybryt.Annotation.before` returns an instance of the
:py:class:`pybryt.BeforeAnnotation<pybryt.annotations.relation.BeforeAnnotation>` class, which is
itself a subclass of :py:class:`pybryt.Annotation` and supports all of the same operations.
:py:class:`pybryt.Annotation` also provides :py:meth:`pybryt.Annotation.after`, which also returns
and instance of the
:py:class:`pybryt.BeforeAnnotation<pybryt.annotations.relation.BeforeAnnotation>` class, but with
the operands switched.
Boolean Annotations
+++++++++++++++++++
Boolean annotations define conditions on the presence of different values. For example, in defining
a solutions, students may be able to take two different paths, and this logic can be enforced
using a :py:class:`pybryt.XorAnnotation<pybryt.annotations.relation.XorAnnotation>` to ensure that
only one of the two possible values is present.
Relational annotations can be created either by instantiating the classes directly using the
constructor or, as is more recommended, by using Python's bitwise logical operators, ``&``, ``|``,
``^``, and ``~``, on annotations. The dunder methods for these operators have been overrided with
for the :py:class:`pybryt.Annotation` class, and return the
:py:class:`pybryt.RelationalAnnotation<pybryt.annotations.relation.RelationalAnnotation>` subclass
instance corresponding to the logical operator used.
To create the xor example above from two values ``v1`` and ``v2``, simply write
.. code-block:: python
v1 ^ v2
To assert that a student should *not* have a specific value ``v`` in their code, use
.. code-block:: python
~v

Просмотреть файл

@ -0,0 +1,23 @@
Reference Implementations
=========================
.. toctree::
:hidden:
:maxdepth: 3
annotations
invariants
reference_objects
PyBryt's core auto-assessment behavior operates by comparing a student's implementation of some
programming problem to a series of reference implementations provided by an instructor. A
**reference implementation** defines a pattern of values, and conditions on those values, to look
for in students' code.
A reference implementation is created by annotating code written or found by an instructor and
executing this code to create a :py:class:`pybryt.ReferenceImplementation` object. Annotations are
created by creating instances of subclasses of the abstract :py:class:`pybryt.Annotation` class.
This section details the creation and behavior of the different annotation classes that PyBryt
provides and describes how reference implementations as a single unit are created, manipulated,
stored, and run.

Просмотреть файл

@ -0,0 +1,4 @@
.. _invariants:
Invariants
==========

Просмотреть файл

@ -0,0 +1,9 @@
Reference Implementation Objects
================================
The ``__init__`` methods of these subclasses automatically add the instances created to a
singleton list that PyBryt maintains, so assigning them to variables or tracking them further is
unnecessary unless more advanced reference implementations are being built. This means that when
marking up code, as below, creating new variables is unnecessary unless further conditions are to
be made later down the line.

Просмотреть файл

@ -2,6 +2,7 @@
"""
from . import invariants
from .annotation import Annotation, AnnotationResult
from .function import Function
from .reference import ReferenceImplementation, ReferenceResult
from .relation import *