_NDArray_1 = NDArray.var("X")\n",
"assume_dtype(_NDArray_1, DType.float64)\n",
"assume_shape(_NDArray_1, TupleInt(Int(1000000)) + TupleInt(Int(20)))\n",
"assume_isfinite(_NDArray_1)\n",
"_NDArray_2 = NDArray.var("y")\n",
"assume_dtype(_NDArray_2, DType.int64)\n",
"assume_shape(_NDArray_2, TupleInt(Int(1000000)))\n",
"assume_value_one_of(_NDArray_2, TupleValue(Value.int(Int(0))) + TupleValue(Value.int(Int(1))))\n",
"_NDArray_3 = astype(\n",
" NDArray.vector(TupleValue(sum(_NDArray_2 == NDArray.scalar(Value.int(Int(0)))).to_value()) + TupleValue(sum(_NDArray_2 == NDArray.scalar(Value.int(Int(1)))).to_value())),\n",
" DType.float64,\n",
") / NDArray.scalar(Value.float(Float(1000000.0)))\n",
"_NDArray_4 = zeros(TupleInt(Int(2)) + TupleInt(Int(20)), OptionalDType.some(DType.float64), OptionalDevice.some(_NDArray_1.device))\n",
"_MultiAxisIndexKey_1 = MultiAxisIndexKey(MultiAxisIndexKeyItem.slice(Slice()))\n",
"_IndexKey_1 = IndexKey.multi_axis(MultiAxisIndexKey(MultiAxisIndexKeyItem.int(Int(0))) + _MultiAxisIndexKey_1)\n",
"_NDArray_5 = _NDArray_1[ndarray_index(_NDArray_2 == NDArray.scalar(Value.int(Int(0))))]\n",
"_OptionalIntOrTuple_1 = OptionalIntOrTuple.some(IntOrTuple.int(Int(0)))\n",
"_NDArray_4[_IndexKey_1] = sum(_NDArray_5, _OptionalIntOrTuple_1) / NDArray.scalar(Value.int(_NDArray_5.shape[Int(0)]))\n",
"_IndexKey_2 = IndexKey.multi_axis(MultiAxisIndexKey(MultiAxisIndexKeyItem.int(Int(1))) + _MultiAxisIndexKey_1)\n",
"_NDArray_6 = _NDArray_1[ndarray_index(_NDArray_2 == NDArray.scalar(Value.int(Int(1))))]\n",
"_NDArray_4[_IndexKey_2] = sum(_NDArray_6, _OptionalIntOrTuple_1) / NDArray.scalar(Value.int(_NDArray_6.shape[Int(0)]))\n",
"_NDArray_7 = concat(TupleNDArray(_NDArray_5 - _NDArray_4[_IndexKey_1]) + TupleNDArray(_NDArray_6 - _NDArray_4[_IndexKey_2]), OptionalInt.some(Int(0)))\n",
"_NDArray_8 = square(_NDArray_7 - expand_dims(sum(_NDArray_7, _OptionalIntOrTuple_1) / NDArray.scalar(Value.int(_NDArray_7.shape[Int(0)]))))\n",
"_NDArray_9 = sqrt(sum(_NDArray_8, _OptionalIntOrTuple_1) / NDArray.scalar(Value.int(_NDArray_8.shape[Int(0)])))\n",
"_NDArray_10 = copy(_NDArray_9)\n",
"_NDArray_10[ndarray_index(_NDArray_9 == NDArray.scalar(Value.int(Int(0))))] = NDArray.scalar(Value.float(Float(1.0)))\n",
"_TupleNDArray_1 = svd(sqrt(NDArray.scalar(Value.float(Float.rational(Rational(1, 999998))))) * (_NDArray_7 / _NDArray_10), FALSE)\n",
"_Slice_1 = Slice(OptionalInt.none, OptionalInt.some(sum(astype(_TupleNDArray_1[Int(1)] > NDArray.scalar(Value.float(Float(0.0001))), DType.int32)).to_value().to_int))\n",
"_NDArray_11 = (_TupleNDArray_1[Int(2)][IndexKey.multi_axis(MultiAxisIndexKey(MultiAxisIndexKeyItem.slice(_Slice_1)) + _MultiAxisIndexKey_1)] / _NDArray_10).T / _TupleNDArray_1[\n",
" Int(1)\n",
"][IndexKey.slice(_Slice_1)]\n",
"_TupleNDArray_2 = svd(\n",
" (sqrt((NDArray.scalar(Value.int(Int(1000000))) * _NDArray_3) * NDArray.scalar(Value.float(Float(1.0)))) * (_NDArray_4 - (_NDArray_3 @ _NDArray_4)).T).T @ _NDArray_11, FALSE\n",
")\n",
"(\n",
" (_NDArray_1 - (_NDArray_3 @ _NDArray_4))\n",
" @ (\n",
" _NDArray_11\n",
" @ _TupleNDArray_2[Int(2)].T[\n",
" IndexKey.multi_axis(\n",
" _MultiAxisIndexKey_1\n",
" + MultiAxisIndexKey(\n",
" MultiAxisIndexKeyItem.slice(\n",
" Slice(\n",
" OptionalInt.none,\n",
" OptionalInt.some(\n",
" sum(astype(_TupleNDArray_2[Int(1)] > (NDArray.scalar(Value.float(Float(0.0001))) * _TupleNDArray_2[Int(1)][IndexKey.int(Int(0))]), DType.int32))\n",
" .to_value()\n",
" .to_int\n",
" ),\n",
" )\n",
" )\n",
" )\n",
" )\n",
" ]\n",
" )\n",
")[IndexKey.multi_axis(_MultiAxisIndexKey_1 + MultiAxisIndexKey(MultiAxisIndexKeyItem.slice(Slice(OptionalInt.none, OptionalInt.some(Int(1))))))]\n",
"
\n"
],
"text/latex": [
"\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n",
"\\PY{n}{\\PYZus{}NDArray\\PYZus{}1} \\PY{o}{=} \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{var}\\PY{p}{(}\\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+s2}{X}\\PY{l+s+s2}{\\PYZdq{}}\\PY{p}{)}\n",
"\\PY{n}{assume\\PYZus{}dtype}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}1}\\PY{p}{,} \\PY{n}{DType}\\PY{o}{.}\\PY{n}{float64}\\PY{p}{)}\n",
"\\PY{n}{assume\\PYZus{}shape}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}1}\\PY{p}{,} \\PY{n}{TupleInt}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{1000000}\\PY{p}{)}\\PY{p}{)} \\PY{o}{+} \\PY{n}{TupleInt}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{20}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\n",
"\\PY{n}{assume\\PYZus{}isfinite}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}1}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}NDArray\\PYZus{}2} \\PY{o}{=} \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{var}\\PY{p}{(}\\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+s2}{y}\\PY{l+s+s2}{\\PYZdq{}}\\PY{p}{)}\n",
"\\PY{n}{assume\\PYZus{}dtype}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}2}\\PY{p}{,} \\PY{n}{DType}\\PY{o}{.}\\PY{n}{int64}\\PY{p}{)}\n",
"\\PY{n}{assume\\PYZus{}shape}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}2}\\PY{p}{,} \\PY{n}{TupleInt}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{1000000}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\n",
"\\PY{n}{assume\\PYZus{}value\\PYZus{}one\\PYZus{}of}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}2}\\PY{p}{,} \\PY{n}{TupleValue}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{0}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)} \\PY{o}{+} \\PY{n}{TupleValue}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{1}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}NDArray\\PYZus{}3} \\PY{o}{=} \\PY{n}{astype}\\PY{p}{(}\n",
" \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{vector}\\PY{p}{(}\\PY{n}{TupleValue}\\PY{p}{(}\\PY{n+nb}{sum}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}2} \\PY{o}{==} \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{0}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{o}{.}\\PY{n}{to\\PYZus{}value}\\PY{p}{(}\\PY{p}{)}\\PY{p}{)} \\PY{o}{+} \\PY{n}{TupleValue}\\PY{p}{(}\\PY{n+nb}{sum}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}2} \\PY{o}{==} \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{1}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{o}{.}\\PY{n}{to\\PYZus{}value}\\PY{p}{(}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{,}\n",
" \\PY{n}{DType}\\PY{o}{.}\\PY{n}{float64}\\PY{p}{,}\n",
"\\PY{p}{)} \\PY{o}{/} \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{float}\\PY{p}{(}\\PY{n}{Float}\\PY{p}{(}\\PY{l+m+mf}{1000000.0}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}NDArray\\PYZus{}4} \\PY{o}{=} \\PY{n}{zeros}\\PY{p}{(}\\PY{n}{TupleInt}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{2}\\PY{p}{)}\\PY{p}{)} \\PY{o}{+} \\PY{n}{TupleInt}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{20}\\PY{p}{)}\\PY{p}{)}\\PY{p}{,} \\PY{n}{OptionalDType}\\PY{o}{.}\\PY{n}{some}\\PY{p}{(}\\PY{n}{DType}\\PY{o}{.}\\PY{n}{float64}\\PY{p}{)}\\PY{p}{,} \\PY{n}{OptionalDevice}\\PY{o}{.}\\PY{n}{some}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}1}\\PY{o}{.}\\PY{n}{device}\\PY{p}{)}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}MultiAxisIndexKey\\PYZus{}1} \\PY{o}{=} \\PY{n}{MultiAxisIndexKey}\\PY{p}{(}\\PY{n}{MultiAxisIndexKeyItem}\\PY{o}{.}\\PY{n}{slice}\\PY{p}{(}\\PY{n}{Slice}\\PY{p}{(}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}IndexKey\\PYZus{}1} \\PY{o}{=} \\PY{n}{IndexKey}\\PY{o}{.}\\PY{n}{multi\\PYZus{}axis}\\PY{p}{(}\\PY{n}{MultiAxisIndexKey}\\PY{p}{(}\\PY{n}{MultiAxisIndexKeyItem}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{0}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)} \\PY{o}{+} \\PY{n}{\\PYZus{}MultiAxisIndexKey\\PYZus{}1}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}NDArray\\PYZus{}5} \\PY{o}{=} \\PY{n}{\\PYZus{}NDArray\\PYZus{}1}\\PY{p}{[}\\PY{n}{ndarray\\PYZus{}index}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}2} \\PY{o}{==} \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{0}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{]}\n",
"\\PY{n}{\\PYZus{}OptionalIntOrTuple\\PYZus{}1} \\PY{o}{=} \\PY{n}{OptionalIntOrTuple}\\PY{o}{.}\\PY{n}{some}\\PY{p}{(}\\PY{n}{IntOrTuple}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{0}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}NDArray\\PYZus{}4}\\PY{p}{[}\\PY{n}{\\PYZus{}IndexKey\\PYZus{}1}\\PY{p}{]} \\PY{o}{=} \\PY{n+nb}{sum}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}5}\\PY{p}{,} \\PY{n}{\\PYZus{}OptionalIntOrTuple\\PYZus{}1}\\PY{p}{)} \\PY{o}{/} \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}5}\\PY{o}{.}\\PY{n}{shape}\\PY{p}{[}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{0}\\PY{p}{)}\\PY{p}{]}\\PY{p}{)}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}IndexKey\\PYZus{}2} \\PY{o}{=} \\PY{n}{IndexKey}\\PY{o}{.}\\PY{n}{multi\\PYZus{}axis}\\PY{p}{(}\\PY{n}{MultiAxisIndexKey}\\PY{p}{(}\\PY{n}{MultiAxisIndexKeyItem}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{1}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)} \\PY{o}{+} \\PY{n}{\\PYZus{}MultiAxisIndexKey\\PYZus{}1}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}NDArray\\PYZus{}6} \\PY{o}{=} \\PY{n}{\\PYZus{}NDArray\\PYZus{}1}\\PY{p}{[}\\PY{n}{ndarray\\PYZus{}index}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}2} \\PY{o}{==} \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{1}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{]}\n",
"\\PY{n}{\\PYZus{}NDArray\\PYZus{}4}\\PY{p}{[}\\PY{n}{\\PYZus{}IndexKey\\PYZus{}2}\\PY{p}{]} \\PY{o}{=} \\PY{n+nb}{sum}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}6}\\PY{p}{,} \\PY{n}{\\PYZus{}OptionalIntOrTuple\\PYZus{}1}\\PY{p}{)} \\PY{o}{/} \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}6}\\PY{o}{.}\\PY{n}{shape}\\PY{p}{[}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{0}\\PY{p}{)}\\PY{p}{]}\\PY{p}{)}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}NDArray\\PYZus{}7} \\PY{o}{=} \\PY{n}{concat}\\PY{p}{(}\\PY{n}{TupleNDArray}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}5} \\PY{o}{\\PYZhy{}} \\PY{n}{\\PYZus{}NDArray\\PYZus{}4}\\PY{p}{[}\\PY{n}{\\PYZus{}IndexKey\\PYZus{}1}\\PY{p}{]}\\PY{p}{)} \\PY{o}{+} \\PY{n}{TupleNDArray}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}6} \\PY{o}{\\PYZhy{}} \\PY{n}{\\PYZus{}NDArray\\PYZus{}4}\\PY{p}{[}\\PY{n}{\\PYZus{}IndexKey\\PYZus{}2}\\PY{p}{]}\\PY{p}{)}\\PY{p}{,} \\PY{n}{OptionalInt}\\PY{o}{.}\\PY{n}{some}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{0}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}NDArray\\PYZus{}8} \\PY{o}{=} \\PY{n}{square}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}7} \\PY{o}{\\PYZhy{}} \\PY{n}{expand\\PYZus{}dims}\\PY{p}{(}\\PY{n+nb}{sum}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}7}\\PY{p}{,} \\PY{n}{\\PYZus{}OptionalIntOrTuple\\PYZus{}1}\\PY{p}{)} \\PY{o}{/} \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}7}\\PY{o}{.}\\PY{n}{shape}\\PY{p}{[}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{0}\\PY{p}{)}\\PY{p}{]}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}NDArray\\PYZus{}9} \\PY{o}{=} \\PY{n}{sqrt}\\PY{p}{(}\\PY{n+nb}{sum}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}8}\\PY{p}{,} \\PY{n}{\\PYZus{}OptionalIntOrTuple\\PYZus{}1}\\PY{p}{)} \\PY{o}{/} \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}8}\\PY{o}{.}\\PY{n}{shape}\\PY{p}{[}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{0}\\PY{p}{)}\\PY{p}{]}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}NDArray\\PYZus{}10} \\PY{o}{=} \\PY{n}{copy}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}9}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}NDArray\\PYZus{}10}\\PY{p}{[}\\PY{n}{ndarray\\PYZus{}index}\\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}9} \\PY{o}{==} \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{0}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{]} \\PY{o}{=} \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{float}\\PY{p}{(}\\PY{n}{Float}\\PY{p}{(}\\PY{l+m+mf}{1.0}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}TupleNDArray\\PYZus{}1} \\PY{o}{=} \\PY{n}{svd}\\PY{p}{(}\\PY{n}{sqrt}\\PY{p}{(}\\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{float}\\PY{p}{(}\\PY{n}{Float}\\PY{o}{.}\\PY{n}{rational}\\PY{p}{(}\\PY{n}{Rational}\\PY{p}{(}\\PY{l+m+mi}{1}\\PY{p}{,} \\PY{l+m+mi}{999998}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)} \\PY{o}{*} \\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}7} \\PY{o}{/} \\PY{n}{\\PYZus{}NDArray\\PYZus{}10}\\PY{p}{)}\\PY{p}{,} \\PY{n}{FALSE}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}Slice\\PYZus{}1} \\PY{o}{=} \\PY{n}{Slice}\\PY{p}{(}\\PY{n}{OptionalInt}\\PY{o}{.}\\PY{n}{none}\\PY{p}{,} \\PY{n}{OptionalInt}\\PY{o}{.}\\PY{n}{some}\\PY{p}{(}\\PY{n+nb}{sum}\\PY{p}{(}\\PY{n}{astype}\\PY{p}{(}\\PY{n}{\\PYZus{}TupleNDArray\\PYZus{}1}\\PY{p}{[}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{1}\\PY{p}{)}\\PY{p}{]} \\PY{o}{\\PYZgt{}} \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{float}\\PY{p}{(}\\PY{n}{Float}\\PY{p}{(}\\PY{l+m+mf}{0.0001}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{,} \\PY{n}{DType}\\PY{o}{.}\\PY{n}{int32}\\PY{p}{)}\\PY{p}{)}\\PY{o}{.}\\PY{n}{to\\PYZus{}value}\\PY{p}{(}\\PY{p}{)}\\PY{o}{.}\\PY{n}{to\\PYZus{}int}\\PY{p}{)}\\PY{p}{)}\n",
"\\PY{n}{\\PYZus{}NDArray\\PYZus{}11} \\PY{o}{=} \\PY{p}{(}\\PY{n}{\\PYZus{}TupleNDArray\\PYZus{}1}\\PY{p}{[}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{2}\\PY{p}{)}\\PY{p}{]}\\PY{p}{[}\\PY{n}{IndexKey}\\PY{o}{.}\\PY{n}{multi\\PYZus{}axis}\\PY{p}{(}\\PY{n}{MultiAxisIndexKey}\\PY{p}{(}\\PY{n}{MultiAxisIndexKeyItem}\\PY{o}{.}\\PY{n}{slice}\\PY{p}{(}\\PY{n}{\\PYZus{}Slice\\PYZus{}1}\\PY{p}{)}\\PY{p}{)} \\PY{o}{+} \\PY{n}{\\PYZus{}MultiAxisIndexKey\\PYZus{}1}\\PY{p}{)}\\PY{p}{]} \\PY{o}{/} \\PY{n}{\\PYZus{}NDArray\\PYZus{}10}\\PY{p}{)}\\PY{o}{.}\\PY{n}{T} \\PY{o}{/} \\PY{n}{\\PYZus{}TupleNDArray\\PYZus{}1}\\PY{p}{[}\n",
" \\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{1}\\PY{p}{)}\n",
"\\PY{p}{]}\\PY{p}{[}\\PY{n}{IndexKey}\\PY{o}{.}\\PY{n}{slice}\\PY{p}{(}\\PY{n}{\\PYZus{}Slice\\PYZus{}1}\\PY{p}{)}\\PY{p}{]}\n",
"\\PY{n}{\\PYZus{}TupleNDArray\\PYZus{}2} \\PY{o}{=} \\PY{n}{svd}\\PY{p}{(}\n",
" \\PY{p}{(}\\PY{n}{sqrt}\\PY{p}{(}\\PY{p}{(}\\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{1000000}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)} \\PY{o}{*} \\PY{n}{\\PYZus{}NDArray\\PYZus{}3}\\PY{p}{)} \\PY{o}{*} \\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{float}\\PY{p}{(}\\PY{n}{Float}\\PY{p}{(}\\PY{l+m+mf}{1.0}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)} \\PY{o}{*} \\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}4} \\PY{o}{\\PYZhy{}} \\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}3} \\PY{o}{@} \\PY{n}{\\PYZus{}NDArray\\PYZus{}4}\\PY{p}{)}\\PY{p}{)}\\PY{o}{.}\\PY{n}{T}\\PY{p}{)}\\PY{o}{.}\\PY{n}{T} \\PY{o}{@} \\PY{n}{\\PYZus{}NDArray\\PYZus{}11}\\PY{p}{,} \\PY{n}{FALSE}\n",
"\\PY{p}{)}\n",
"\\PY{p}{(}\n",
" \\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}1} \\PY{o}{\\PYZhy{}} \\PY{p}{(}\\PY{n}{\\PYZus{}NDArray\\PYZus{}3} \\PY{o}{@} \\PY{n}{\\PYZus{}NDArray\\PYZus{}4}\\PY{p}{)}\\PY{p}{)}\n",
" \\PY{o}{@} \\PY{p}{(}\n",
" \\PY{n}{\\PYZus{}NDArray\\PYZus{}11}\n",
" \\PY{o}{@} \\PY{n}{\\PYZus{}TupleNDArray\\PYZus{}2}\\PY{p}{[}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{2}\\PY{p}{)}\\PY{p}{]}\\PY{o}{.}\\PY{n}{T}\\PY{p}{[}\n",
" \\PY{n}{IndexKey}\\PY{o}{.}\\PY{n}{multi\\PYZus{}axis}\\PY{p}{(}\n",
" \\PY{n}{\\PYZus{}MultiAxisIndexKey\\PYZus{}1}\n",
" \\PY{o}{+} \\PY{n}{MultiAxisIndexKey}\\PY{p}{(}\n",
" \\PY{n}{MultiAxisIndexKeyItem}\\PY{o}{.}\\PY{n}{slice}\\PY{p}{(}\n",
" \\PY{n}{Slice}\\PY{p}{(}\n",
" \\PY{n}{OptionalInt}\\PY{o}{.}\\PY{n}{none}\\PY{p}{,}\n",
" \\PY{n}{OptionalInt}\\PY{o}{.}\\PY{n}{some}\\PY{p}{(}\n",
" \\PY{n+nb}{sum}\\PY{p}{(}\\PY{n}{astype}\\PY{p}{(}\\PY{n}{\\PYZus{}TupleNDArray\\PYZus{}2}\\PY{p}{[}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{1}\\PY{p}{)}\\PY{p}{]} \\PY{o}{\\PYZgt{}} \\PY{p}{(}\\PY{n}{NDArray}\\PY{o}{.}\\PY{n}{scalar}\\PY{p}{(}\\PY{n}{Value}\\PY{o}{.}\\PY{n}{float}\\PY{p}{(}\\PY{n}{Float}\\PY{p}{(}\\PY{l+m+mf}{0.0001}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)} \\PY{o}{*} \\PY{n}{\\PYZus{}TupleNDArray\\PYZus{}2}\\PY{p}{[}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{1}\\PY{p}{)}\\PY{p}{]}\\PY{p}{[}\\PY{n}{IndexKey}\\PY{o}{.}\\PY{n}{int}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{0}\\PY{p}{)}\\PY{p}{)}\\PY{p}{]}\\PY{p}{)}\\PY{p}{,} \\PY{n}{DType}\\PY{o}{.}\\PY{n}{int32}\\PY{p}{)}\\PY{p}{)}\n",
" \\PY{o}{.}\\PY{n}{to\\PYZus{}value}\\PY{p}{(}\\PY{p}{)}\n",
" \\PY{o}{.}\\PY{n}{to\\PYZus{}int}\n",
" \\PY{p}{)}\\PY{p}{,}\n",
" \\PY{p}{)}\n",
" \\PY{p}{)}\n",
" \\PY{p}{)}\n",
" \\PY{p}{)}\n",
" \\PY{p}{]}\n",
" \\PY{p}{)}\n",
"\\PY{p}{)}\\PY{p}{[}\\PY{n}{IndexKey}\\PY{o}{.}\\PY{n}{multi\\PYZus{}axis}\\PY{p}{(}\\PY{n}{\\PYZus{}MultiAxisIndexKey\\PYZus{}1} \\PY{o}{+} \\PY{n}{MultiAxisIndexKey}\\PY{p}{(}\\PY{n}{MultiAxisIndexKeyItem}\\PY{o}{.}\\PY{n}{slice}\\PY{p}{(}\\PY{n}{Slice}\\PY{p}{(}\\PY{n}{OptionalInt}\\PY{o}{.}\\PY{n}{none}\\PY{p}{,} \\PY{n}{OptionalInt}\\PY{o}{.}\\PY{n}{some}\\PY{p}{(}\\PY{n}{Int}\\PY{p}{(}\\PY{l+m+mi}{1}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{)}\\PY{p}{]}\n",
"\\end{Verbatim}\n"
],
"text/plain": [
"_NDArray_1 = NDArray.var(\"X\")\n",
"assume_dtype(_NDArray_1, DType.float64)\n",
"assume_shape(_NDArray_1, TupleInt(Int(1000000)) + TupleInt(Int(20)))\n",
"assume_isfinite(_NDArray_1)\n",
"_NDArray_2 = NDArray.var(\"y\")\n",
"assume_dtype(_NDArray_2, DType.int64)\n",
"assume_shape(_NDArray_2, TupleInt(Int(1000000)))\n",
"assume_value_one_of(_NDArray_2, TupleValue(Value.int(Int(0))) + TupleValue(Value.int(Int(1))))\n",
"_NDArray_3 = astype(\n",
" NDArray.vector(TupleValue(sum(_NDArray_2 == NDArray.scalar(Value.int(Int(0)))).to_value()) + TupleValue(sum(_NDArray_2 == NDArray.scalar(Value.int(Int(1)))).to_value())),\n",
" DType.float64,\n",
") / NDArray.scalar(Value.float(Float(1000000.0)))\n",
"_NDArray_4 = zeros(TupleInt(Int(2)) + TupleInt(Int(20)), OptionalDType.some(DType.float64), OptionalDevice.some(_NDArray_1.device))\n",
"_MultiAxisIndexKey_1 = MultiAxisIndexKey(MultiAxisIndexKeyItem.slice(Slice()))\n",
"_IndexKey_1 = IndexKey.multi_axis(MultiAxisIndexKey(MultiAxisIndexKeyItem.int(Int(0))) + _MultiAxisIndexKey_1)\n",
"_NDArray_5 = _NDArray_1[ndarray_index(_NDArray_2 == NDArray.scalar(Value.int(Int(0))))]\n",
"_OptionalIntOrTuple_1 = OptionalIntOrTuple.some(IntOrTuple.int(Int(0)))\n",
"_NDArray_4[_IndexKey_1] = sum(_NDArray_5, _OptionalIntOrTuple_1) / NDArray.scalar(Value.int(_NDArray_5.shape[Int(0)]))\n",
"_IndexKey_2 = IndexKey.multi_axis(MultiAxisIndexKey(MultiAxisIndexKeyItem.int(Int(1))) + _MultiAxisIndexKey_1)\n",
"_NDArray_6 = _NDArray_1[ndarray_index(_NDArray_2 == NDArray.scalar(Value.int(Int(1))))]\n",
"_NDArray_4[_IndexKey_2] = sum(_NDArray_6, _OptionalIntOrTuple_1) / NDArray.scalar(Value.int(_NDArray_6.shape[Int(0)]))\n",
"_NDArray_7 = concat(TupleNDArray(_NDArray_5 - _NDArray_4[_IndexKey_1]) + TupleNDArray(_NDArray_6 - _NDArray_4[_IndexKey_2]), OptionalInt.some(Int(0)))\n",
"_NDArray_8 = square(_NDArray_7 - expand_dims(sum(_NDArray_7, _OptionalIntOrTuple_1) / NDArray.scalar(Value.int(_NDArray_7.shape[Int(0)]))))\n",
"_NDArray_9 = sqrt(sum(_NDArray_8, _OptionalIntOrTuple_1) / NDArray.scalar(Value.int(_NDArray_8.shape[Int(0)])))\n",
"_NDArray_10 = copy(_NDArray_9)\n",
"_NDArray_10[ndarray_index(_NDArray_9 == NDArray.scalar(Value.int(Int(0))))] = NDArray.scalar(Value.float(Float(1.0)))\n",
"_TupleNDArray_1 = svd(sqrt(NDArray.scalar(Value.float(Float.rational(Rational(1, 999998))))) * (_NDArray_7 / _NDArray_10), FALSE)\n",
"_Slice_1 = Slice(OptionalInt.none, OptionalInt.some(sum(astype(_TupleNDArray_1[Int(1)] > NDArray.scalar(Value.float(Float(0.0001))), DType.int32)).to_value().to_int))\n",
"_NDArray_11 = (_TupleNDArray_1[Int(2)][IndexKey.multi_axis(MultiAxisIndexKey(MultiAxisIndexKeyItem.slice(_Slice_1)) + _MultiAxisIndexKey_1)] / _NDArray_10).T / _TupleNDArray_1[\n",
" Int(1)\n",
"][IndexKey.slice(_Slice_1)]\n",
"_TupleNDArray_2 = svd(\n",
" (sqrt((NDArray.scalar(Value.int(Int(1000000))) * _NDArray_3) * NDArray.scalar(Value.float(Float(1.0)))) * (_NDArray_4 - (_NDArray_3 @ _NDArray_4)).T).T @ _NDArray_11, FALSE\n",
")\n",
"(\n",
" (_NDArray_1 - (_NDArray_3 @ _NDArray_4))\n",
" @ (\n",
" _NDArray_11\n",
" @ _TupleNDArray_2[Int(2)].T[\n",
" IndexKey.multi_axis(\n",
" _MultiAxisIndexKey_1\n",
" + MultiAxisIndexKey(\n",
" MultiAxisIndexKeyItem.slice(\n",
" Slice(\n",
" OptionalInt.none,\n",
" OptionalInt.some(\n",
" sum(astype(_TupleNDArray_2[Int(1)] > (NDArray.scalar(Value.float(Float(0.0001))) * _TupleNDArray_2[Int(1)][IndexKey.int(Int(0))]), DType.int32))\n",
" .to_value()\n",
" .to_int\n",
" ),\n",
" )\n",
" )\n",
" )\n",
" )\n",
" ]\n",
" )\n",
")[IndexKey.multi_axis(_MultiAxisIndexKey_1 + MultiAxisIndexKey(MultiAxisIndexKeyItem.slice(Slice(OptionalInt.none, OptionalInt.some(Int(1))))))]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"optimized_fn.expr"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "c5c4994b",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"def __fn(X, y):\n",
" assert X.dtype == np.dtype(np.float64)\n",
" assert X.shape == (1000000, 20,)\n",
" assert np.all(np.isfinite(X))\n",
" assert y.dtype == np.dtype(np.int64)\n",
" assert y.shape == (1000000,)\n",
" assert set(np.unique(y)) == set((0, 1,))\n",
" _0 = y == np.array(0)\n",
" _1 = np.sum(_0)\n",
" _2 = y == np.array(1)\n",
" _3 = np.sum(_2)\n",
" _4 = np.array((_1, _3,)).astype(np.dtype(np.float64))\n",
" _5 = _4 / np.array(1000000.0)\n",
" _6 = np.zeros((2, 20,), dtype=np.dtype(np.float64))\n",
" _7 = np.sum(X[_0], axis=0)\n",
" _8 = _7 / np.array(X[_0].shape[0])\n",
" _6[0, :] = _8\n",
" _9 = np.sum(X[_2], axis=0)\n",
" _10 = _9 / np.array(X[_2].shape[0])\n",
" _6[1, :] = _10\n",
" _11 = _5 @ _6\n",
" _12 = X - _11\n",
" _13 = np.sqrt(np.array(float(1 / 999998)))\n",
" _14 = X[_0] - _6[0, :]\n",
" _15 = X[_2] - _6[1, :]\n",
" _16 = np.concatenate((_14, _15,), axis=0)\n",
" _17 = np.sum(_16, axis=0)\n",
" _18 = _17 / np.array(_16.shape[0])\n",
" _19 = np.expand_dims(_18, 0)\n",
" _20 = _16 - _19\n",
" _21 = np.square(_20)\n",
" _22 = np.sum(_21, axis=0)\n",
" _23 = _22 / np.array(_21.shape[0])\n",
" _24 = np.sqrt(_23)\n",
" _25 = _24 == np.array(0)\n",
" _24[_25] = np.array(1.0)\n",
" _26 = _16 / _24\n",
" _27 = _13 * _26\n",
" _28 = np.linalg.svd(_27, full_matrices=False)\n",
" _29 = _28[1] > np.array(0.0001)\n",
" _30 = _29.astype(np.dtype(np.int32))\n",
" _31 = np.sum(_30)\n",
" _32 = _28[2][:_31, :] / _24\n",
" _33 = _32.T / _28[1][:_31]\n",
" _34 = np.array(1000000) * _5\n",
" _35 = _34 * np.array(1.0)\n",
" _36 = np.sqrt(_35)\n",
" _37 = _6 - _11\n",
" _38 = _36 * _37.T\n",
" _39 = _38.T @ _33\n",
" _40 = np.linalg.svd(_39, full_matrices=False)\n",
" _41 = np.array(0.0001) * _40[1][0]\n",
" _42 = _40[1] > _41\n",
" _43 = _42.astype(np.dtype(np.int32))\n",
" _44 = np.sum(_43)\n",
" _45 = _33 @ _40[2].T[:, :_44]\n",
" _46 = _12 @ _45\n",
" return _46[:, :1]\n",
"\n"
]
}
],
"source": [
"import inspect\n",
"\n",
"print(inspect.getsource(optimized_fn))"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "83212722",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/var/folders/xn/05ktz3056kqd9n8frgd6236h0000gn/T/egglog-9befda2c-765c-4e2c-94d5-f667be45c7eb.py:56: NumbaPerformanceWarning: '@' is faster on contiguous arrays, called on (Array(float64, 2, 'C', False, aligned=True), Array(float64, 2, 'A', False, aligned=True))\n",
" _45 = _33 @ _40[2].T[:, :_44]\n"
]
}
],
"source": [
"import numba\n",
"import numpy as np\n",
"\n",
"numba_fn = numba.njit(fastmath=True)(optimized_fn)\n",
"assert np.allclose(run_lda(X_np, y_np), numba_fn(X_np, y_np))"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "3b08ba55",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"import timeit\n",
"import pandas as pd\n",
"import seaborn as sns\n",
"\n",
"stmts = {\n",
" \"original\": \"run_lda(X_np, y_np)\",\n",
" \"numba\": \"numba_fn(X_np, y_np)\",\n",
"}\n",
"df = pd.DataFrame.from_dict(\n",
" {name: timeit.repeat(stmt, globals=globals(), number=1, repeat=10) for name, stmt in stmts.items()}\n",
")\n",
"df_melt = pd.melt(df, var_name=\"function\", value_name=\"time\")"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "2b79af51",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"data": {
"image/png": "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gVuWpba2NknS5s2bdf3112vChAn6+9//rm3btunuu+/Wf//73y6pBThTJ/v33KPHsV/P9nHXjU821+L4c1iWdcrvSGccOnRIubm5io+P15o1a/T2229r3bp1ksR3yQEENYI2ePBgRUdHa9OmTf62I0eO6O2339bFF1/cqXN4PB4lJyf7J6BJUmtrq955550zqu2NN97Q+eefr7vvvlsjRoxQenq6/vWvf53ROQEnnHPOOZKOPUXwc8dPLDtTW7ZsCdh+8803lZ6erqioKH3wwQf65JNPtGTJEl1++eW66KKLmEjmIG7PQtD69eunn/3sZ7rzzjuVmJiogQMHaunSpfrss8904403avv27Z06z6233qri4mKlpaXpoosu0oMPPqj//Oc/AcNxwUpPT1ddXZ2eeuopXXLJJVq/fr2/JwBEkj59+ujSSy/VkiVLdMEFF+jAgQOaN29eyM5fV1enwsJCTZ8+Xe+8844efPBB3X///ZKkgQMHKjo6Wg8++KBmzJihd999V7/4xS9C9tkIDj1qnJYlS5bohz/8oX7yk5/oa1/7mmpqalRWVqazzjqr0+eYO3eurrvuOk2ePFmjRo1SbGyscnNz1bt379Ou63vf+57uuOMO3XLLLRo+fLjeeOMNzZ8//7TPBzjpT3/6k44ePars7Gzdfvvtuvfee0N27smTJ+vw4cMaOXKkZs6cqdtuu00//elPJR3rza9atUpPP/20Lr74Yi1ZskS//vWvQ/bZCA6PuYQx2traNGTIEE2cOJH/vQPA/8fQNxzzr3/9Sy+99JLGjBmjlpYWLV++XHv27NGkSZOcLg0AjMHQNxzTo0cPrVq1SpdccolGjx6tHTt26OWXX9aQIUOcLg0AjMHQNwAABqNHDQCAwQhqAAAMRlADAGAwghoAAIMR1AAAGIygBlzEtm399Kc/VWJioizLCuna0MHYu3evo58PuAm3ZwEu8sILL+jqq69WRUWFLrzwQn3pS19Sz55du67R1KlTdfDgQZWWlvrbWltb9X//939h+XzA7fgGAS5SW1ur/v376xvf+IajdURFRSklJcXRGgC3YOgbcImpU6fq1ltvVV1dnSzL0qBBgzRo0CA98MADAfsNHz5cixYt8m9blqVHHnlE3//+99W3b1+lp6frueeeCzimurpa3/3udxUfH6+4uDhdfvnlqq2t1aJFi/TYY4/p2WeflWVZsixLFRUVHQ59v/rqqxo5cqRiYmLUv39/3XXXXTp69Kj//bFjx2rWrFmaM2eOEhMTlZKSElAn0F0R1IBLLFu2TPfcc4/OO+881dfXBzzr+4ssXrxYEydO1D//+U9NmDBB119/vT799FNJ0v79+/XNb35TMTExeuWVV1RZWalp06bp6NGjmj17tiZOnKjvfOc7qq+vV319fYe9+f3792vChAm65JJLtH37dj300EP64x//2O5pUI899pj69eunLVu2aOnSpbrnnnu0YcOGM/uLASIcQ9+AS3g8HsXFxZ3WsPPUqVN13XXXSZLuu+8+/e53v9Nbb72l73znO1qxYoU8Ho+eeuop9erVS5KUkZHhP7ZPnz5qaWk55WeWlJQoNTVVy5cvl2VZuuiii/Tvf/9bc+fO1YIFC9Sjx7E+w7Bhw7Rw4UJJx54tvnz5cv3jH//QFVdcEdTPA7gJPWoAGjZsmP/P/fr1U3x8vA4cOCBJqqqq0uWXX+4P6dPx/vvva9SoUbIsy982evRo+Xw+ffTRRx3WIUn9+/f31wF0VwQ14GI9evTQiTd2HDlypN1+J4awZVlqa2uTdKzHHC6nqgPorghqwMXOOecc1dfX+7e9Xq/27NkT1DmGDRum1157rcOAl6To6Gi1trae8hxDhgzR5s2bA/7TsGnTJsXFxem8884Lqh6guyGoARcbN26cVq9erddee007duzQlClTFBUVFdQ5brnlFnm9Xl177bXaunWrdu3apdWrV+vDDz+UJA0aNEj//Oc/9eGHH+rjjz/uMNALCgq0b98+3Xrrrfrggw/07LPPauHChSosLPRfnwbQMb4hgIsVFRVpzJgx+u53v6urrrpK11xzjQYPHhzUOc4++2y98sor8vl8GjNmjLKzs/WHP/zBP0x9880368tf/rJGjBihc845R5s2bWp3jnPPPVfPP/+83nrrLWVmZmrGjBm68cYbNW/evJD8nICbsTIZAAAGo0cNAIDBCGoAAAxGUAMAYDCCGgAAgxHUAAAYjKAGAMBgBDUAAAYjqAEAMBhBDQCAwQhqAAAMRlADAGCw/wcHfPYCynu7UQAAAABJRU5ErkJggg==",
"text/plain": [
"