Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add derivatives, incl. high-order and mixed partial #3701

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/support_table.md
Expand Up @@ -360,6 +360,7 @@ use `\ce` instead|
|\downharpoonleft|$\downharpoonleft$||
|\downharpoonright|$\downharpoonright$||
|{drcases}|$\begin{drcases}a&\text{if }b\\c&\text{if }d\end{drcases}$|`\begin{drcases}`<br>&nbsp;&nbsp;&nbsp;`a &\text{if } b \\`<br>&nbsp;&nbsp;&nbsp;`c &\text{if } d`<br>`\end{drcases}`|
|\dv|$\dv{f}{t} \dv[2]{y}{x}$|`\dv{f}{t} \dv[2]{y}{x}`|

## E

Expand Down Expand Up @@ -791,6 +792,7 @@ use `\ce` instead|
|\O|$\text{\O}$|`\text{\O}`|
|\o|$\text{\o}$|`\text{\o}`|
|\odot|$\odot$||
|\odv|$\odv{f}{t} \odv[2]{y}{x}$|`\odv{f}{t} \odv[2]{y}{x}`|
|\OE|$\text{\OE}$|`\text{\OE}`|
|\oe|$\text{\oe}$|`\text{\oe}`|
|\officialeuro|<span style="color:firebrick;">Not supported</span>||
Expand Down Expand Up @@ -837,6 +839,7 @@ use `\ce` instead|
|\parallel|$\parallel$||
|\part|<span style="color:firebrick;">Not supported</span>|[Deprecated](https://en.wikipedia.org/wiki/Help:Displaying_a_formula#Deprecated_syntax)|
|\partial|$\partial$||
|\pdv|$\pdv{f}{t} \pdv[2,2][4]{\psi}{x, y}$|`\pdv{f}{t} \pdv[2,2][4]{\psi}{x, y}`|
|\perp|$\perp$||
|\phantom|$\Gamma^{\phantom{i}j}_{i\phantom{j}k}$|`\Gamma^{\phantom{i}j}_{i\phantom{j}k}`|
|\phase|$\phase{-78^\circ}$|`\phase{-78^\circ}`|
Expand Down
34 changes: 34 additions & 0 deletions docs/supported.md
Expand Up @@ -559,6 +559,40 @@ Direct Input: $← ↑ → ↓ ↔ ↕ ↖ ↗ ↘ ↙ ↚ ↛ ↞ ↠ ↢ ↣

Extensible arrows all can take an optional argument in the same manner<br>as `\xrightarrow[under]{over}`.

## Derivatives

The syntax closely emulates popular $LaTeX$ packages that provide derivatives,
such as `physics`, `diffcoef`, and `derivative`.

|||
|:----------|:----------|
|$\odv{}{t}$ `\odv{}{t}` |$\odv[2]{}{x}$ `\odv[2]{}{x}` |
|$\odv{f}{t}$ `\odv{f}{t}` |$\odv[2]{y}{x}$ `\odv[2]{y}{x}`|
|$\pdv{}{t}$ `\pdv{}{t}` |$\pdv[2]{}{x}$ `\pdv[2]{}{x}` |
|$\pdv{f}{t}$ `\pdv{f}{t}` |$\pdv[2]{y}{x}$ `\pdv[2]{y}{x}`|
|$\pdv[1,1][2]{}{x,y}$ `\pdv[1,1][2]{}{x,y}` |$\pdv[1,n][1+n]{}{x,y}$ `\pdv[1,n][1+n]{}{x,y}` |
|$\pdv[1,1][2]{z}{x,y}$ `\pdv[1,1][2]{z}{x,y}`|$\pdv[1,n][1+n]{z}{x,y}$ `\pdv[1,n][1+n]{z}{x,y}`|
|$\pdv[1,N-1][N]{z}{x_{\mu,\sigma},y_\xi}$ `\pdv[1,N-1][N]{z}{x_{\mu,\sigma},y_\xi}`||

Ordinary derivative `\odv[order]{f}{x}`:
* The first required brace `{f}` holds the function to take derivative of. This
brace can be left empty.
* The second required brace `{x}` holds the variable.
* `[order]` holds the derivative order. If the order is 1, you may omit this
bracket.

Partial derivative `\pdv[var_orders][total_order]{f}{vars}`:
* The first required brace `{f}` holds the function to take derivative of. This
brace can be left empty.
* The second required brace `{vars}` holds the comma-separated variables, e.g.
`{x}`, `{x, y, z_k}`.
* The first optional braket `[var_orders]` holds the list of derivative orders
corresponding to the variables, e.g. `[2]`, `[1, n, m+1]`. If there is only
one variable and its order is 1, you may omit this bracket.
* The second optional braket `[total_order]` holds the total order of the
derivative. If there is only one variable order and that is the same as the
total order, you may omit this bracket.

## Special Notation

**Bra-ket Notation**
Expand Down
17 changes: 17 additions & 0 deletions src/functions/genfrac.js
Expand Up @@ -11,6 +11,8 @@ import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import {calculateSize, makeEm} from "../units";

import {derivativeHandler} from './utils/assembleDerivative';

const adjustStyle = (size, originalStyle) => {
// Figure out what style this fraction should be in based on the
// function used
Expand Down Expand Up @@ -508,3 +510,18 @@ defineFunction({
htmlBuilder,
mathmlBuilder,
});

defineFunction({
type: "genfrac",
names: [
"\\dv", "\\odv", // Ordinary derivative.
"\\pdv", // Partial derivative.
],
props: {
numArgs: 2, // Function, variable(s).
numOptionalArgs: 2, // Lower index(es), upper index.
},
handler: derivativeHandler,
htmlBuilder,
mathmlBuilder,
});
137 changes: 137 additions & 0 deletions src/functions/utils/assembleDerivative.js
@@ -0,0 +1,137 @@
// @flow

import {assertNodeType} from "../../parseNode";

import type {AnyParseNode, ParseNode} from "../../parseNode";
import type {FunctionContext} from "../../defineFunction";

const groupByComma = (node: AnyParseNode): AnyParseNode[][] => {
if (node.type !== "ordgroup") {
return [[node]];
}

// Dives into the body list of this ParseNode<"ordgroup">, then groups the
// member nodes using comma as the delimiter:
// ["1", "+", "a", ",", "n"] => [ ["1", "+", "a" ], ["n"] ]
const nodesArr = assertNodeType(node, "ordgroup").body;
const res = [];
let currentChunk: AnyParseNode[] = [];
for (let i = 0; i < nodesArr.length; ++i) {
const e: AnyParseNode = nodesArr[i];
const isDelimiter = e.type === "atom" && e.text === ",";
if (!isDelimiter) {
currentChunk.push(e);
}
if (isDelimiter || i === nodesArr.length - 1) {
res.push(currentChunk);
currentChunk = [];
}
}
return res;
};

const MATH_ONE: ParseNode<"textord"> = {
type: "textord",
mode: "math",
text: "1",
};
const PLACEHOLDER: ParseNode<"textord"> = {
type: "textord",
mode: "math",
text: "\\square",
};

export function derivativeHandler(
{parser, funcName}: FunctionContext,
args: AnyParseNode[],
optArgs: (?AnyParseNode)[],
): ParseNode<"genfrac"> {
const makeOrdGroup =
(bodyArr: AnyParseNode[]): ParseNode<"ordgroup"> => ({
type: "ordgroup",
mode: parser.mode,
body: bodyArr,
});

const func = args[0];
const vars = args[1];

const lowerDiffOrder: AnyParseNode =
optArgs[0] ?? makeOrdGroup([MATH_ONE]);
const totalDiffOrder: AnyParseNode =
optArgs[1] ?? lowerDiffOrder;

let symbol;
switch (funcName) {
case "\\dv":
case "\\odv":
symbol = "d";
break;
case "\\pdv":
symbol = "\\partial";
break;
default:
throw new Error("Unrecognized differential command");
}

const dVars = groupByComma(vars);
const numVar = dVars.length;
const dIndices = groupByComma(lowerDiffOrder);
const numIndices = dIndices.length;

if (numVar < numIndices) {
dVars.push(...Array(numIndices - numVar).fill([PLACEHOLDER]));
} else if (numVar > numIndices) {
dIndices.push(...Array(numVar - numIndices).fill([PLACEHOLDER]));
}

const makeDGroup = (base, sup) => {
if (!sup) {
return base;
}
if (sup.type === "ordgroup") {
const supBody = ((sup: any): ParseNode<"ordgroup">).body;
if (supBody.length === 0) {
supBody.push(PLACEHOLDER);
} else if (supBody.length === 1
&& supBody[0].mode === "math"
&& supBody[0].text === "1") {
return base; // Omit the index if derivative order is 1.
}
}
return {
type: "supsub",
mode: parser.mode,
base,
sup,
};
};
const d = {
type: "textord",
mode: parser.mode,
text: symbol,
};

const numer = makeOrdGroup([
makeDGroup(d, totalDiffOrder), func]);
const denom = makeOrdGroup(dVars.map((variable: AnyParseNode[], i) => {
const index: AnyParseNode[] = dIndices[i];
return makeDGroup(
makeOrdGroup([d].concat(makeOrdGroup(variable))),
makeOrdGroup(index),
);
}));

return {
type: "genfrac",
mode: parser.mode,
continued: false,
numer,
denom,
hasBarLine: true,
leftDelim: null,
rightDelim: null,
size: "auto",
barSize: null,
};
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/screenshotter/images/Derivatives-firefox.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions test/screenshotter/ss_data.yaml
Expand Up @@ -125,6 +125,16 @@ DelimiterSizing: |
a & b & c\\
a & b & c\\
\end{pmatrix}\; \Braket{ ϕ | \frac{∂^2}{∂ t^2} | ψ } \\
Derivatives: |
\dv{}{x},\odv{y}{x},\odv[2]{}{x},\odv[n+1]{y}{x} \\
\pdv{}{x},\pdv{y}{x},\pdv[2]{}{x},\pdv[n+1]{y}{x} \\
\pdv[1,1][2]{z}{x,y},\pdv[2,2][4]{z}{x,y}, \pdv[1,a][1+a]{z}{x,y} \\
\pdv[1+2^n,{(1,2)},k,1][N_k+1]{\begin{pmatrix}\psi&0\\0&\phi\end{pmatrix}} {x,y,z_{\mu,\sigma},\tau} \\
DerivativesFallbacks: |
\square = \text{placeholder} \\
\odv[]{y}{x}, \pdv[]{y}{x} \\
\pdv[2][4]{z}{x,y}, \pdv[2,2][]{z}{x,y}, \pdv[2,2][4]{z}{x} \\
\pdv[2][6]{\psi}{x,y,z}, \pdv[2,2][]{\psi}{x,y,z}, \pdv[2,2,2][6]{\psi}{x} \\
DisplayMode:
tex: \sum_{i=0}^\infty \frac{1}{i}
pre: pre
Expand Down