Repository: kpeeters/cadabra2
Branch: master
Commit: 0afcc652660a
Files: 1369
Total size: 21.6 MB
Directory structure:
gitextract_noaq8p8m/
├── .editorconfig
├── .gitattributes
├── .github/
│ └── workflows/
│ ├── appimage-modern.yml
│ ├── c++lib.yml
│ ├── docker.yml
│ ├── fedora-40-package.yml
│ ├── fedora-41-package.yml
│ ├── fedora-42-package.yml
│ ├── freebsd.yml
│ ├── homebrew-devel.yml
│ ├── homebrew.yml
│ ├── linux.yml
│ ├── macos.yml
│ ├── opensuse-tumbleweed-package.yml
│ ├── tarball.yml
│ ├── ubuntu-22.04-package.yml
│ ├── ubuntu-24.04-package.yml
│ ├── windows-installer.yml
│ └── windows.yml
├── .gitignore
├── .gitmodules
├── .travis.yml
├── CITATION.cff
├── CMakeLists.txt
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── JUPYTER.rst
├── LICENSE
├── Makefile
├── README.rst
├── c++lib/
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── README.txt
│ ├── SympyDummy.cc
│ ├── adjform.cc
│ ├── cpplib.hh.in
│ ├── nevaluate.cc
│ ├── nevaluate.py
│ ├── simple.cc
│ └── trivial.cc
├── client_server/
│ ├── Actions.cc
│ ├── Actions.hh
│ ├── CMakeLists.txt
│ ├── ComputeThread.cc
│ ├── ComputeThread.hh
│ ├── DocumentThread.cc
│ ├── DocumentThread.hh
│ ├── GUIBase.hh
│ ├── ScriptThread.cc
│ ├── ScriptThread.hh
│ ├── Server.cc
│ ├── Server.hh
│ ├── Snoop.cc
│ ├── Snoop.hh
│ ├── TODO
│ ├── cadabra-jupyter-kernel.cc
│ ├── cadabra-jupyter-kernel.hh
│ ├── cadabra-jupyter-main.cc
│ ├── cadabra-server.cc
│ ├── cadabra2html.cc
│ ├── cadabra2latex.cc
│ ├── connection.json
│ ├── kernel.json
│ ├── notebook.html
│ ├── notebook.tex
│ ├── popen2.cc
│ ├── popen2.hh
│ ├── regexp_tester.cc
│ ├── test_client.cc
│ ├── test_talk_to_server.cc
│ ├── tree.hh
│ ├── websocket_client.cc
│ ├── websocket_client.hh
│ ├── websocket_server.cc
│ └── websocket_server.hh
├── cmake/
│ ├── cmake_uninstall.cmake.in
│ ├── functions.cmake
│ ├── modules/
│ │ ├── FindGLIBMM3.cmake
│ │ ├── FindGLIBMM4.cmake
│ │ ├── FindGMPXX.cmake
│ │ ├── FindGTKMM3.cmake
│ │ ├── FindGTKMM4.cmake
│ │ ├── FindJSONCPP.cmake
│ │ ├── FindLibPythonOSX.py
│ │ ├── FindMathematica.cmake
│ │ ├── FindMathematicaDocumentationBuild.cmake.in
│ │ ├── FindMathematicaTestDriver.cmd
│ │ ├── FindMathematicaTestDriver.sh
│ │ ├── FindPythonLibsOSX.cmake
│ │ ├── FindSQLITE3.cmake
│ │ ├── FindZeroMQ.cmake
│ │ └── cotire.cmake
│ ├── packaging.cmake
│ ├── policies.cmake
│ ├── version.cmake
│ └── windows.cmake
├── codemeta.json
├── conda/
│ ├── build.sh
│ └── meta.yaml
├── config/
│ ├── AppRun
│ ├── Doxyfile
│ ├── DoxygenLayout.xml
│ ├── DoxygenStyle.css
│ ├── README.txt
│ ├── buildbot.sh
│ ├── buildpkg.sh
│ ├── doxyrest-config.lua
│ ├── generate_keywords.py
│ ├── init-cadabra2.scm
│ ├── install_python_windows.cmake.in
│ ├── install_script.iss.in
│ ├── make.bat
│ ├── post_install.rtf
│ ├── postinst.in
│ ├── pre_install.rtf.in
│ ├── publish-doxygen
│ ├── science.cadabra.cadabra2-gtk.desktop.in
│ ├── shortcuts.wxs
│ ├── travisci_rsa.enc
│ └── travisci_rsa.pub
├── contrib/
│ ├── einstein_equations.cnb
│ └── structure_equations_and_bianchi.cnb
├── core/
│ ├── .gitignore
│ ├── Adjform.cc
│ ├── Adjform.hh
│ ├── Algorithm.cc
│ ├── Algorithm.hh
│ ├── Bridge.cc
│ ├── Bridge.hh
│ ├── CMakeLists.txt
│ ├── CdbPython.cc
│ ├── CdbPython.hh
│ ├── Cleanup.cc
│ ├── Cleanup.hh
│ ├── Combinatorics.cc
│ ├── Combinatorics.hh
│ ├── Compare.cc
│ ├── Compare.hh
│ ├── Config.hh.in
│ ├── DataCell.cc
│ ├── DataCell.hh
│ ├── Debug.hh
│ ├── DisplayBase.cc
│ ├── DisplayBase.hh
│ ├── DisplayMMA.cc
│ ├── DisplayMMA.hh
│ ├── DisplaySympy.cc
│ ├── DisplaySympy.hh
│ ├── DisplayTeX.cc
│ ├── DisplayTeX.hh
│ ├── DisplayTerminal.cc
│ ├── DisplayTerminal.hh
│ ├── Dummies.hh
│ ├── Equals.cc
│ ├── Equals.hh
│ ├── ExManip.cc
│ ├── ExManip.hh
│ ├── ExNode.cc
│ ├── ExNode.hh
│ ├── Exceptions.cc
│ ├── Exceptions.hh
│ ├── Exchange.cc
│ ├── Exchange.hh
│ ├── Functional.cc
│ ├── Functional.hh
│ ├── Grouping.cc
│ ├── Grouping.hh
│ ├── Hash.cc
│ ├── Hash.hh
│ ├── IndexClassifier.cc
│ ├── IndexClassifier.hh
│ ├── IndexIterator.cc
│ ├── IndexIterator.hh
│ ├── InstallPrefix.cc
│ ├── InstallPrefix.hh
│ ├── Kernel.cc
│ ├── Kernel.hh
│ ├── Linear.cc
│ ├── Linear.hh
│ ├── MMACdb.cc
│ ├── MMACdb.hh
│ ├── Media.cc
│ ├── Media.hh
│ ├── MultiIndex.hh
│ ├── Multiplier.cc
│ ├── Multiplier.hh
│ ├── NDSolver.cc
│ ├── NDSolver.hh
│ ├── NEvaluator.cc
│ ├── NEvaluator.hh
│ ├── NIntegrator.cc
│ ├── NIntegrator.hh
│ ├── NInterpolatingFunction.cc
│ ├── NInterpolatingFunction.hh
│ ├── NTensor.cc
│ ├── NTensor.hh
│ ├── Parser.cc
│ ├── Parser.hh
│ ├── Permutations.hh
│ ├── PreClean.cc
│ ├── PreClean.hh
│ ├── PreProcessor.cc
│ ├── PreProcessor.hh
│ ├── ProgressMonitor.cc
│ ├── ProgressMonitor.hh
│ ├── Props.cc
│ ├── Props.hh
│ ├── PythonException.cc
│ ├── PythonException.hh
│ ├── ReservedNode.cc
│ ├── ReservedNode.hh
│ ├── Stopwatch.cc
│ ├── Stopwatch.hh
│ ├── Storage.cc
│ ├── Storage.hh
│ ├── Sum.cc
│ ├── Sum.hh
│ ├── Symbols.cc
│ ├── Symbols.hh
│ ├── SympyCdb.cc
│ ├── SympyCdb.hh
│ ├── TerminalStream.cc
│ ├── TerminalStream.hh
│ ├── YoungTab.cc
│ ├── YoungTab.hh
│ ├── algorithms/
│ │ ├── all_contractions.tex
│ │ ├── asym.cnb
│ │ ├── canonicalise.cc
│ │ ├── canonicalise.cnb
│ │ ├── canonicalise.hh
│ │ ├── canonicalorder.tex
│ │ ├── coefficients.tex
│ │ ├── collect_components.cc
│ │ ├── collect_components.hh
│ │ ├── collect_factors.cc
│ │ ├── collect_factors.cnb
│ │ ├── collect_factors.hh
│ │ ├── collect_terms.cc
│ │ ├── collect_terms.cnb
│ │ ├── collect_terms.hh
│ │ ├── combine.cc
│ │ ├── combine.cnb
│ │ ├── combine.hh
│ │ ├── complete.cc
│ │ ├── complete.cnb
│ │ ├── complete.hh
│ │ ├── component.hh
│ │ ├── decompose.cc
│ │ ├── decompose.cnb
│ │ ├── decompose.hh
│ │ ├── decompose_product.cc
│ │ ├── decompose_product.cnb
│ │ ├── decompose_product.hh
│ │ ├── depprint.tex
│ │ ├── distribute.cc
│ │ ├── distribute.cnb
│ │ ├── distribute.hh
│ │ ├── drop_weight.cc
│ │ ├── drop_weight.cnb
│ │ ├── drop_weight.hh
│ │ ├── dualise_tensor.tex
│ │ ├── einsteinify.cc
│ │ ├── einsteinify.cnb
│ │ ├── einsteinify.hh
│ │ ├── eliminate_kronecker.cc
│ │ ├── eliminate_kronecker.cnb
│ │ ├── eliminate_kronecker.hh
│ │ ├── eliminate_metric.cc
│ │ ├── eliminate_metric.cnb
│ │ ├── eliminate_metric.hh
│ │ ├── eliminate_vielbein.cc
│ │ ├── eliminate_vielbein.cnb
│ │ ├── eliminate_vielbein.hh
│ │ ├── eliminate_vielbein.tex
│ │ ├── eliminateeps.tex
│ │ ├── epsilon_to_delta.cc
│ │ ├── epsilon_to_delta.cnb
│ │ ├── epsilon_to_delta.hh
│ │ ├── evaluate.cc
│ │ ├── evaluate.cnb
│ │ ├── evaluate.hh
│ │ ├── expand.cc
│ │ ├── expand.cnb
│ │ ├── expand.hh
│ │ ├── expand_delta.cc
│ │ ├── expand_delta.cnb
│ │ ├── expand_delta.hh
│ │ ├── expand_diracbar.cc
│ │ ├── expand_diracbar.cnb
│ │ ├── expand_diracbar.hh
│ │ ├── expand_dummies.cc
│ │ ├── expand_dummies.cnb
│ │ ├── expand_dummies.hh
│ │ ├── expand_power.cc
│ │ ├── expand_power.cnb
│ │ ├── expand_power.hh
│ │ ├── expand_product_shorthand.tex
│ │ ├── explicit_indices.cc
│ │ ├── explicit_indices.cnb
│ │ ├── explicit_indices.hh
│ │ ├── factor_in.cc
│ │ ├── factor_in.cnb
│ │ ├── factor_in.hh
│ │ ├── factor_out.cc
│ │ ├── factor_out.cnb
│ │ ├── factor_out.hh
│ │ ├── fierz.cc
│ │ ├── fierz.cnb
│ │ ├── fierz.hh
│ │ ├── first_order_form.cc
│ │ ├── first_order_form.hh
│ │ ├── flatten_product.cc
│ │ ├── flatten_product.hh
│ │ ├── flatten_sum.cc
│ │ ├── flatten_sum.hh
│ │ ├── impose_asym.tex
│ │ ├── impose_bianchi.tex
│ │ ├── index_rename.tex
│ │ ├── indexlist.tex
│ │ ├── indexsort.cc
│ │ ├── indexsort.hh
│ │ ├── indexsort.tex
│ │ ├── inner.tex
│ │ ├── integrate_by_parts.cc
│ │ ├── integrate_by_parts.cnb
│ │ ├── integrate_by_parts.hh
│ │ ├── join_gamma.cc
│ │ ├── join_gamma.cnb
│ │ ├── join_gamma.hh
│ │ ├── keep_terms.cc
│ │ ├── keep_terms.hh
│ │ ├── keep_terms.tex
│ │ ├── keep_weight.cnb
│ │ ├── list_sum.tex
│ │ ├── listflatten.tex
│ │ ├── lower_free_indices.cc
│ │ ├── lower_free_indices.cnb
│ │ ├── lower_free_indices.hh
│ │ ├── lr_tensor.cc
│ │ ├── lr_tensor.cnb
│ │ ├── lr_tensor.hh
│ │ ├── lsolve.tex
│ │ ├── map_mma.cc
│ │ ├── map_mma.hh
│ │ ├── map_sympy.cc
│ │ ├── map_sympy.cnb
│ │ ├── map_sympy.hh
│ │ ├── meld.cc
│ │ ├── meld.cnb
│ │ ├── meld.hh
│ │ ├── ndsolve.cnb
│ │ ├── nevaluate.cc
│ │ ├── nevaluate.cnb
│ │ ├── nevaluate.hh
│ │ ├── nval.cc
│ │ ├── nval.cnb
│ │ ├── nval.hh
│ │ ├── order.cc
│ │ ├── order.hh
│ │ ├── order.tex
│ │ ├── permute.tex
│ │ ├── product_rule.cc
│ │ ├── product_rule.cnb
│ │ ├── product_rule.hh
│ │ ├── product_shorthand.tex
│ │ ├── projweyl.tex
│ │ ├── properties.tex
│ │ ├── proplist.tex
│ │ ├── raise_free_indices.cnb
│ │ ├── range.tex
│ │ ├── reduce.tex
│ │ ├── reduce_delta.cc
│ │ ├── reduce_delta.cnb
│ │ ├── reduce_delta.hh
│ │ ├── remove_indexbracket.tex
│ │ ├── remove_weyl_traces.tex
│ │ ├── rename_dummies.cc
│ │ ├── rename_dummies.cnb
│ │ ├── rename_dummies.hh
│ │ ├── replace_match.cc
│ │ ├── replace_match.cnb
│ │ ├── replace_match.hh
│ │ ├── rewrite_indices.cc
│ │ ├── rewrite_indices.cnb
│ │ ├── rewrite_indices.hh
│ │ ├── simplify.cc
│ │ ├── simplify.cnb
│ │ ├── simplify.hh
│ │ ├── slot_asym.cnb
│ │ ├── sort_product.cc
│ │ ├── sort_product.cnb
│ │ ├── sort_product.hh
│ │ ├── sort_spinors.cc
│ │ ├── sort_spinors.cnb
│ │ ├── sort_spinors.hh
│ │ ├── sort_sum.cc
│ │ ├── sort_sum.cnb
│ │ ├── sort_sum.hh
│ │ ├── split.cc
│ │ ├── split.hh
│ │ ├── split_gamma.cc
│ │ ├── split_gamma.cnb
│ │ ├── split_gamma.hh
│ │ ├── split_index.cc
│ │ ├── split_index.cnb
│ │ ├── split_index.hh
│ │ ├── substitute.cc
│ │ ├── substitute.cnb
│ │ ├── substitute.hh
│ │ ├── sumflatten.tex
│ │ ├── sym.cc
│ │ ├── sym.hh
│ │ ├── sym.tex
│ │ ├── tab_basics.cc
│ │ ├── tab_basics.hh
│ │ ├── tab_dimension.cc
│ │ ├── tab_dimension.hh
│ │ ├── tabcanonicalise.tex
│ │ ├── tabdimension.tex
│ │ ├── tabstandardform.tex
│ │ ├── take_match.cc
│ │ ├── take_match.cnb
│ │ ├── take_match.hh
│ │ ├── tree.tex
│ │ ├── unique_indices.tex
│ │ ├── untrace.cc
│ │ ├── untrace.cnb
│ │ ├── untrace.hh
│ │ ├── unwrap.cc
│ │ ├── unwrap.cnb
│ │ ├── unwrap.hh
│ │ ├── unzoom.cc
│ │ ├── unzoom.hh
│ │ ├── vary.cc
│ │ ├── vary.cnb
│ │ ├── vary.hh
│ │ ├── weyl_index_order.tex
│ │ ├── young_project.cc
│ │ ├── young_project.hh
│ │ ├── young_project.tex
│ │ ├── young_project_product.cc
│ │ ├── young_project_product.cnb
│ │ ├── young_project_product.hh
│ │ ├── young_project_tensor.cc
│ │ ├── young_project_tensor.cnb
│ │ ├── young_project_tensor.hh
│ │ ├── zoom.cc
│ │ ├── zoom.cnb
│ │ └── zoom.hh
│ ├── cadabra2-cli.cc
│ ├── cadabra2-cli.hh
│ ├── cadabra2.in
│ ├── cadabra2_defaults.py.in
│ ├── cadabra2cadabra.cc
│ ├── cadabra2ipynb.cc
│ ├── cadabra2python.cc
│ ├── cdb-nbtool.cc
│ ├── echokernel.py
│ ├── lru_cache.hh
│ ├── modules/
│ │ ├── Lie.cc
│ │ ├── xperm_new.cc
│ │ └── xperm_new.h
│ ├── packages/
│ │ ├── CMakeLists.txt
│ │ └── cdb/
│ │ ├── core/
│ │ │ ├── _component.cc
│ │ │ ├── component.cnb
│ │ │ ├── manip.cnb
│ │ │ ├── solve.cnb
│ │ │ └── trace.cnb
│ │ ├── gauge_theory/
│ │ │ └── instantons.cnb
│ │ ├── graphics/
│ │ │ └── plot.cnb
│ │ ├── interact/
│ │ │ └── slider.cnb
│ │ ├── main.py
│ │ ├── numeric/
│ │ │ ├── evaluate.cnb
│ │ │ └── integrate.cnb
│ │ ├── relativity/
│ │ │ ├── __init__.cdb
│ │ │ ├── abstract.cnb
│ │ │ └── schwarzschild.cnb
│ │ ├── remote/
│ │ │ ├── __init__.py
│ │ │ ├── highlight.py
│ │ │ ├── record.py
│ │ │ └── speech.py
│ │ ├── sympy/
│ │ │ ├── calculus.cnb
│ │ │ └── solvers.cnb
│ │ └── utils/
│ │ ├── _algorithm.cc
│ │ ├── develop.cnb
│ │ ├── indices.cnb
│ │ ├── node.cnb
│ │ ├── tableau.cnb
│ │ └── types.cnb
│ ├── passing.cc
│ ├── properties/
│ │ ├── Accent.cc
│ │ ├── Accent.cnb
│ │ ├── Accent.hh
│ │ ├── Accent.tex
│ │ ├── AntiCommuting.cc
│ │ ├── AntiCommuting.cnb
│ │ ├── AntiCommuting.hh
│ │ ├── AntiCommuting.tex
│ │ ├── AntiSelfDual.tex
│ │ ├── AntiSymmetric.cc
│ │ ├── AntiSymmetric.cnb
│ │ ├── AntiSymmetric.hh
│ │ ├── AntiSymmetric.tex
│ │ ├── Commuting.cc
│ │ ├── Commuting.cnb
│ │ ├── Commuting.hh
│ │ ├── Commuting.tex
│ │ ├── CommutingAsProduct.cc
│ │ ├── CommutingAsProduct.cnb
│ │ ├── CommutingAsProduct.hh
│ │ ├── CommutingAsProduct.tex
│ │ ├── CommutingAsSum.cc
│ │ ├── CommutingAsSum.cnb
│ │ ├── CommutingAsSum.hh
│ │ ├── CommutingAsSum.tex
│ │ ├── CommutingBehaviour.cc
│ │ ├── CommutingBehaviour.hh
│ │ ├── Coordinate.cc
│ │ ├── Coordinate.cnb
│ │ ├── Coordinate.hh
│ │ ├── Coordinate.tex
│ │ ├── DAntiSymmetric.cc
│ │ ├── DAntiSymmetric.cnb
│ │ ├── DAntiSymmetric.hh
│ │ ├── DAntiSymmetric.tex
│ │ ├── Depends.cc
│ │ ├── Depends.cnb
│ │ ├── Depends.hh
│ │ ├── Depends.tex
│ │ ├── DependsBase.hh
│ │ ├── DependsInherit.cc
│ │ ├── DependsInherit.hh
│ │ ├── DependsInherit.tex
│ │ ├── Derivative.cc
│ │ ├── Derivative.cnb
│ │ ├── Derivative.hh
│ │ ├── Derivative.tex
│ │ ├── DerivativeOp.cc
│ │ ├── DerivativeOp.hh
│ │ ├── Determinant.cc
│ │ ├── Determinant.cnb
│ │ ├── Determinant.hh
│ │ ├── Diagonal.cc
│ │ ├── Diagonal.cnb
│ │ ├── Diagonal.hh
│ │ ├── Diagonal.tex
│ │ ├── DifferentialForm.cc
│ │ ├── DifferentialForm.hh
│ │ ├── DifferentialFormBase.hh
│ │ ├── DiracBar.cc
│ │ ├── DiracBar.cnb
│ │ ├── DiracBar.hh
│ │ ├── DiracBar.tex
│ │ ├── Distributable.cc
│ │ ├── Distributable.cnb
│ │ ├── Distributable.hh
│ │ ├── Distributable.tex
│ │ ├── EpsilonTensor.cc
│ │ ├── EpsilonTensor.cnb
│ │ ├── EpsilonTensor.hh
│ │ ├── EpsilonTensor.tex
│ │ ├── ExteriorDerivative.cc
│ │ ├── ExteriorDerivative.hh
│ │ ├── FilledTableau.cc
│ │ ├── FilledTableau.cnb
│ │ ├── FilledTableau.hh
│ │ ├── FilledTableau.tex
│ │ ├── GammaMatrix.cc
│ │ ├── GammaMatrix.cnb
│ │ ├── GammaMatrix.hh
│ │ ├── GammaMatrix.tex
│ │ ├── GammaTraceless.cc
│ │ ├── GammaTraceless.hh
│ │ ├── GammaTraceless.tex
│ │ ├── ImaginaryI.cc
│ │ ├── ImaginaryI.hh
│ │ ├── ImplicitIndex.cc
│ │ ├── ImplicitIndex.cnb
│ │ ├── ImplicitIndex.hh
│ │ ├── ImplicitIndex.tex
│ │ ├── IndexInherit.cc
│ │ ├── IndexInherit.cnb
│ │ ├── IndexInherit.hh
│ │ ├── IndexInherit.tex
│ │ ├── Indices.cc
│ │ ├── Indices.cnb
│ │ ├── Indices.hh
│ │ ├── Indices.tex
│ │ ├── Integer.cc
│ │ ├── Integer.cnb
│ │ ├── Integer.hh
│ │ ├── Integer.tex
│ │ ├── Integral.hh
│ │ ├── InverseMetric.cc
│ │ ├── InverseMetric.cnb
│ │ ├── InverseMetric.hh
│ │ ├── InverseMetric.tex
│ │ ├── InverseVielbein.cnb
│ │ ├── InverseVielbein.tex
│ │ ├── KeepHistory.tex
│ │ ├── KroneckerDelta.cc
│ │ ├── KroneckerDelta.cnb
│ │ ├── KroneckerDelta.hh
│ │ ├── KroneckerDelta.tex
│ │ ├── LaTeXForm.cc
│ │ ├── LaTeXForm.cnb
│ │ ├── LaTeXForm.hh
│ │ ├── LaTeXForm.tex
│ │ ├── Matrix.cc
│ │ ├── Matrix.hh
│ │ ├── Matrix.tex
│ │ ├── Metric.cc
│ │ ├── Metric.cnb
│ │ ├── Metric.hh
│ │ ├── Metric.tex
│ │ ├── NonCommuting.cc
│ │ ├── NonCommuting.cnb
│ │ ├── NonCommuting.hh
│ │ ├── NonCommuting.tex
│ │ ├── NumericalFlat.cc
│ │ ├── NumericalFlat.hh
│ │ ├── NumericalFlat.tex
│ │ ├── PartialDerivative.cc
│ │ ├── PartialDerivative.cnb
│ │ ├── PartialDerivative.hh
│ │ ├── PartialDerivative.tex
│ │ ├── PostDefaultRules.tex
│ │ ├── PreDefaultRules.tex
│ │ ├── PropertyInherit.tex
│ │ ├── RiemannTensor.cc
│ │ ├── RiemannTensor.cnb
│ │ ├── RiemannTensor.hh
│ │ ├── RiemannTensor.tex
│ │ ├── SatisfiesBianchi.cc
│ │ ├── SatisfiesBianchi.cnb
│ │ ├── SatisfiesBianchi.hh
│ │ ├── SatisfiesBianchi.tex
│ │ ├── SelfAntiCommuting.cc
│ │ ├── SelfAntiCommuting.cnb
│ │ ├── SelfAntiCommuting.hh
│ │ ├── SelfAntiCommuting.tex
│ │ ├── SelfCommuting.cc
│ │ ├── SelfCommuting.cnb
│ │ ├── SelfCommuting.hh
│ │ ├── SelfCommuting.tex
│ │ ├── SelfCommutingBehaviour.hh
│ │ ├── SelfDual.tex
│ │ ├── SelfNonCommuting.cc
│ │ ├── SelfNonCommuting.cnb
│ │ ├── SelfNonCommuting.hh
│ │ ├── SelfNonCommuting.tex
│ │ ├── SigmaBarMatrix.tex
│ │ ├── SigmaMatrix.hh
│ │ ├── SigmaMatrix.tex
│ │ ├── SortOrder.cc
│ │ ├── SortOrder.cnb
│ │ ├── SortOrder.hh
│ │ ├── SortOrder.tex
│ │ ├── Spinor.cc
│ │ ├── Spinor.cnb
│ │ ├── Spinor.hh
│ │ ├── Spinor.tex
│ │ ├── Symbol.cc
│ │ ├── Symbol.cnb
│ │ ├── Symbol.hh
│ │ ├── Symmetric.cc
│ │ ├── Symmetric.cnb
│ │ ├── Symmetric.hh
│ │ ├── Symmetric.tex
│ │ ├── Tableau.cc
│ │ ├── Tableau.cnb
│ │ ├── Tableau.hh
│ │ ├── TableauBase.cc
│ │ ├── TableauBase.hh
│ │ ├── TableauInherit.cc
│ │ ├── TableauInherit.hh
│ │ ├── TableauSymmetry.cc
│ │ ├── TableauSymmetry.cnb
│ │ ├── TableauSymmetry.hh
│ │ ├── TableauSymmetry.tex
│ │ ├── Trace.cc
│ │ ├── Trace.cnb
│ │ ├── Trace.hh
│ │ ├── Traceless.cc
│ │ ├── Traceless.hh
│ │ ├── Traceless.tex
│ │ ├── Vielbein.cc
│ │ ├── Vielbein.cnb
│ │ ├── Vielbein.hh
│ │ ├── Vielbein.tex
│ │ ├── Weight.cc
│ │ ├── Weight.cnb
│ │ ├── Weight.hh
│ │ ├── WeightBase.hh
│ │ ├── WeightInherit.cc
│ │ ├── WeightInherit.cnb
│ │ ├── WeightInherit.hh
│ │ ├── WeylTensor.cc
│ │ ├── WeylTensor.hh
│ │ └── WeylTensor.tex
│ ├── pythoncdb/
│ │ ├── py_algorithms.cc
│ │ ├── py_algorithms.hh
│ │ ├── py_ex.cc
│ │ ├── py_ex.hh
│ │ ├── py_globals.cc
│ │ ├── py_globals.hh
│ │ ├── py_helpers.cc
│ │ ├── py_helpers.hh
│ │ ├── py_kernel.cc
│ │ ├── py_kernel.hh
│ │ ├── py_media.cc
│ │ ├── py_media.hh
│ │ ├── py_module.cc
│ │ ├── py_ntensor.cc
│ │ ├── py_ntensor.hh
│ │ ├── py_packages.cc
│ │ ├── py_packages.hh
│ │ ├── py_progress.cc
│ │ ├── py_progress.hh
│ │ ├── py_properties.cc
│ │ ├── py_properties.hh
│ │ ├── py_stopwatch.cc
│ │ ├── py_stopwatch.hh
│ │ ├── py_tableau.cc
│ │ └── py_tableau.hh
│ ├── test_benchmark.cc
│ ├── test_compile_command.py
│ ├── test_internals.cc
│ ├── test_multiindex.cc
│ ├── test_multiplier.cc
│ ├── test_permutations.cc
│ ├── test_preprocessor.cc
│ ├── test_wstp.cc
│ └── tree.hh
├── doc/
│ ├── .gitignore
│ ├── adjacency_form.md
│ ├── autogobble.sty
│ ├── cadabra2.tex
│ ├── cadabra2_hep.tex
│ ├── description
│ ├── license.txt
│ ├── main.md
│ ├── modules.dox
│ ├── random.md
│ ├── reserved/
│ │ ├── anticommutator.tex
│ │ ├── arrow.tex
│ │ ├── cdot.tex
│ │ ├── comma.tex
│ │ ├── commutator.tex
│ │ ├── conditional.tex
│ │ ├── equals.tex
│ │ ├── expression.tex
│ │ ├── factorial.tex
│ │ ├── frac.tex
│ │ ├── indexbracket.tex
│ │ ├── infty.tex
│ │ ├── label.tex
│ │ ├── matrix.tex
│ │ ├── pow.tex
│ │ ├── prod.tex
│ │ ├── regex.tex
│ │ ├── sequence.tex
│ │ ├── sum.tex
│ │ └── unequals.tex
│ ├── tableaux.sty
│ ├── the_cadabra_book.bib
│ ├── the_cadabra_book.tex
│ ├── users/
│ │ ├── command_line.tex
│ │ ├── comparison.tex
│ │ ├── components.tex
│ │ ├── input.tex
│ │ └── notebook_comparisons.tex
│ └── writing_algorithms.tex
├── docker/
│ ├── Dockerfile
│ └── entrypoint.sh
├── examples/
│ ├── .gitignore
│ ├── auto_meld.cnb
│ ├── automatic_multiterm.cnb
│ ├── beginners.cnb
│ ├── bianchi_identities.cnb
│ ├── canonicalise.cnb
│ ├── cell_ids.cnb
│ ├── component_evaluation.cnb
│ ├── components2.cnb
│ ├── converge.cnb
│ ├── covariant_derivative.cdb
│ ├── equations_of_motion.cnb
│ ├── exterior.cnb
│ ├── fermionic_oscillator_algebra.cnb
│ ├── fierz.cnb
│ ├── for_previous_users.cnb
│ ├── frw.cnb
│ ├── gamma_matrix_algebra.cnb
│ ├── gamma_traces.cnb
│ ├── graphical_user_interface.cnb
│ ├── ho.cnb
│ ├── indexing_expressions.cnb
│ ├── input_format.cnb
│ ├── kaluza_klein.cnb
│ ├── kerr.cnb
│ ├── library.cnb
│ ├── lovelock.cnb
│ ├── nintegrate.cnb
│ ├── numerics.cnb
│ ├── packages.cnb
│ ├── packages2.cnb
│ ├── plotting.cnb
│ ├── poincare_algebra.cnb
│ ├── post_processing.cnb
│ ├── quickstart.cnb
│ ├── ref_accents.cnb
│ ├── ref_c++_library.cnb
│ ├── ref_core_package.cnb
│ ├── ref_default_simplification.cnb
│ ├── ref_derivatives.cnb
│ ├── ref_dynamical_updates.cnb
│ ├── ref_exponents.cnb
│ ├── ref_flags_variables.cnb
│ ├── ref_implicit_versus_explicit.cnb
│ ├── ref_import.cnb
│ ├── ref_indexbrackets.cnb
│ ├── ref_indices.cnb
│ ├── ref_kernel.cnb
│ ├── ref_ndsolve.cnb
│ ├── ref_numerical.cnb
│ ├── ref_ordering.cnb
│ ├── ref_patterns.cnb
│ ├── ref_plotting.cnb
│ ├── ref_printing.cnb
│ ├── ref_programming.cnb
│ ├── ref_properties.cnb
│ ├── ref_selecting.cnb
│ ├── ref_spacing.cnb
│ ├── ref_sympy.cnb
│ ├── reset.cnb
│ ├── sample_dyn.cnb
│ ├── scalar_manipulations.cnb
│ ├── scalar_manipulations2.cnb
│ ├── schwarzschild.cnb
│ ├── schwarzschild.ipynb
│ ├── simple_evaluate.cnb
│ ├── slider.cnb
│ ├── sphere.cnb
│ ├── spinors.cnb
│ ├── string_states.cnb
│ ├── super_maxwell.cnb
│ ├── supergravity.cnb
│ ├── sympy_bridge.cnb
│ ├── sympy_examples.cnb
│ ├── tensor_monomials.cnb
│ ├── tensors_in_denominators.cnb
│ ├── typesetting.cnb
│ ├── utf8.cnb
│ ├── vacuum_einstein_first_order.cnb
│ ├── variational_derivatives.cnb
│ ├── working_with_ex.cnb
│ └── world-sheet_susy.cnb
├── frontend/
│ ├── CMakeLists.txt
│ ├── common/
│ │ ├── CMakeLists.txt
│ │ ├── TeXEngine.cc
│ │ ├── TeXEngine.hh
│ │ ├── lodepng.cc
│ │ ├── lodepng.h
│ │ ├── preamble.tex
│ │ ├── test_tex.cc
│ │ └── testpre.tex
│ ├── gtkmm/
│ │ ├── CMakeLists.txt
│ │ ├── Cadabra.cc
│ │ ├── Cadabra.hh
│ │ ├── ChooseColoursDialog.cc
│ │ ├── ChooseColoursDialog.hh
│ │ ├── CodeInput.cc
│ │ ├── CodeInput.hh
│ │ ├── Console.cc
│ │ ├── Console.hh
│ │ ├── DiffViewer.cc
│ │ ├── DiffViewer.hh
│ │ ├── ImageView.cc
│ │ ├── ImageView.hh
│ │ ├── Keywords.cc
│ │ ├── Keywords.hh
│ │ ├── NotebookCanvas.cc
│ │ ├── NotebookCanvas.hh
│ │ ├── NotebookWindow.cc
│ │ ├── NotebookWindow.hh
│ │ ├── Preferences.hh
│ │ ├── SelectFileDialog.cc
│ │ ├── SelectFileDialog.hh
│ │ ├── SliderView.cc
│ │ ├── SliderView.hh
│ │ ├── TeXView.cc
│ │ ├── TeXView.hh
│ │ ├── VisualCell.hh
│ │ ├── cadabra2-gtk.appdata.xml.in
│ │ ├── cdb-icons/
│ │ │ └── README.md
│ │ ├── config/
│ │ │ ├── gschemas.compiled
│ │ │ └── settings.ini
│ │ ├── icons/
│ │ │ ├── Adwaita/
│ │ │ │ ├── cursors/
│ │ │ │ │ ├── 00008160000006810000408080010102.cur
│ │ │ │ │ ├── 028006030e0e7ebffc7f7070c0600140.cur
│ │ │ │ │ ├── 03b6e0fcb3499374a867c041f52298f0.cur
│ │ │ │ │ ├── 08e8e1c95fe2fc01f976f1e063a24ccd.ani
│ │ │ │ │ ├── 1081e37283d90000800003c07f3ef6bf.cur
│ │ │ │ │ ├── 14fef782d02440884392942c11205230.cur
│ │ │ │ │ ├── 2870a09082c103050810ffdffffe0204.cur
│ │ │ │ │ ├── 3085a0e285430894940527032f8b26df.cur
│ │ │ │ │ ├── 3ecb610c1bf2410f44200f48c40d3599.ani
│ │ │ │ │ ├── 4498f0e0c1937ffe01fd06f973665830.cur
│ │ │ │ │ ├── 5c6cd98b3f3ebcb1f9c7f1c204630408.cur
│ │ │ │ │ ├── 6407b0e94181790501fd1e167b474872.cur
│ │ │ │ │ ├── 640fb0e74195791501fd1ed57b41487f.cur
│ │ │ │ │ ├── 9081237383d90e509aa00f00170e968f.cur
│ │ │ │ │ ├── 9d800788f1b08800ae810202380a0822.cur
│ │ │ │ │ ├── X_cursor.cur
│ │ │ │ │ ├── alias.cur
│ │ │ │ │ ├── all-scroll.cur
│ │ │ │ │ ├── arrow.cur
│ │ │ │ │ ├── bd_double_arrow.cur
│ │ │ │ │ ├── bottom_left_corner.cur
│ │ │ │ │ ├── bottom_right_corner.cur
│ │ │ │ │ ├── bottom_side.cur
│ │ │ │ │ ├── bottom_tee.cur
│ │ │ │ │ ├── c7088f0f3e6c8088236ef8e1e3e70000.cur
│ │ │ │ │ ├── cell.cur
│ │ │ │ │ ├── circle.cur
│ │ │ │ │ ├── col-resize.cur
│ │ │ │ │ ├── context-menu.cur
│ │ │ │ │ ├── copy.cur
│ │ │ │ │ ├── cross.cur
│ │ │ │ │ ├── cross_reverse.cur
│ │ │ │ │ ├── crossed_circle.cur
│ │ │ │ │ ├── crosshair.cur
│ │ │ │ │ ├── d9ce0ab605698f320427677b458ad60b.cur
│ │ │ │ │ ├── default.cur
│ │ │ │ │ ├── diamond_cross.cur
│ │ │ │ │ ├── dnd-ask.cur
│ │ │ │ │ ├── dnd-copy.cur
│ │ │ │ │ ├── dnd-link.cur
│ │ │ │ │ ├── dnd-move.cur
│ │ │ │ │ ├── dnd-no-drop.cur
│ │ │ │ │ ├── dnd-none.cur
│ │ │ │ │ ├── dot_box_mask.cur
│ │ │ │ │ ├── dotbox.cur
│ │ │ │ │ ├── double_arrow.cur
│ │ │ │ │ ├── draft_large.cur
│ │ │ │ │ ├── draft_small.cur
│ │ │ │ │ ├── draped_box.cur
│ │ │ │ │ ├── e-resize.cur
│ │ │ │ │ ├── e29285e634086352946a0e7090d73106.cur
│ │ │ │ │ ├── ew-resize.cur
│ │ │ │ │ ├── fcf1c3c7cd4491d801f1e1c78f100000.cur
│ │ │ │ │ ├── fd_double_arrow.cur
│ │ │ │ │ ├── fleur.cur
│ │ │ │ │ ├── grab.cur
│ │ │ │ │ ├── grabbing.cur
│ │ │ │ │ ├── h_double_arrow.cur
│ │ │ │ │ ├── hand.cur
│ │ │ │ │ ├── hand1.cur
│ │ │ │ │ ├── hand2.cur
│ │ │ │ │ ├── help.cur
│ │ │ │ │ ├── icon.cur
│ │ │ │ │ ├── left_ptr.cur
│ │ │ │ │ ├── left_ptr_help.cur
│ │ │ │ │ ├── left_ptr_watch.ani
│ │ │ │ │ ├── left_side.cur
│ │ │ │ │ ├── left_tee.cur
│ │ │ │ │ ├── link.cur
│ │ │ │ │ ├── ll_angle.cur
│ │ │ │ │ ├── lr_angle.cur
│ │ │ │ │ ├── move.cur
│ │ │ │ │ ├── n-resize.cur
│ │ │ │ │ ├── ne-resize.cur
│ │ │ │ │ ├── nesw-resize.cur
│ │ │ │ │ ├── no-drop.cur
│ │ │ │ │ ├── not-allowed.cur
│ │ │ │ │ ├── ns-resize.cur
│ │ │ │ │ ├── nw-resize.cur
│ │ │ │ │ ├── nwse-resize.cur
│ │ │ │ │ ├── pencil.cur
│ │ │ │ │ ├── pirate.cur
│ │ │ │ │ ├── plus.cur
│ │ │ │ │ ├── pointer-move.cur
│ │ │ │ │ ├── pointer.cur
│ │ │ │ │ ├── progress.ani
│ │ │ │ │ ├── question_arrow.cur
│ │ │ │ │ ├── right_ptr.cur
│ │ │ │ │ ├── right_side.cur
│ │ │ │ │ ├── right_tee.cur
│ │ │ │ │ ├── row-resize.cur
│ │ │ │ │ ├── s-resize.cur
│ │ │ │ │ ├── sb_down_arrow.cur
│ │ │ │ │ ├── sb_h_double_arrow.cur
│ │ │ │ │ ├── sb_left_arrow.cur
│ │ │ │ │ ├── sb_right_arrow.cur
│ │ │ │ │ ├── sb_up_arrow.cur
│ │ │ │ │ ├── sb_v_double_arrow.cur
│ │ │ │ │ ├── se-resize.cur
│ │ │ │ │ ├── size_all.cur
│ │ │ │ │ ├── size_bdiag.cur
│ │ │ │ │ ├── size_fdiag.cur
│ │ │ │ │ ├── size_hor.cur
│ │ │ │ │ ├── size_ver.cur
│ │ │ │ │ ├── sw-resize.cur
│ │ │ │ │ ├── target.cur
│ │ │ │ │ ├── tcross.cur
│ │ │ │ │ ├── text.cur
│ │ │ │ │ ├── top_left_arrow.cur
│ │ │ │ │ ├── top_left_corner.cur
│ │ │ │ │ ├── top_right_corner.cur
│ │ │ │ │ ├── top_side.cur
│ │ │ │ │ ├── top_tee.cur
│ │ │ │ │ ├── ul_angle.cur
│ │ │ │ │ ├── ur_angle.cur
│ │ │ │ │ ├── v_double_arrow.cur
│ │ │ │ │ ├── vertical-text.cur
│ │ │ │ │ ├── w-resize.cur
│ │ │ │ │ ├── wait.ani
│ │ │ │ │ ├── watch.ani
│ │ │ │ │ ├── xterm.cur
│ │ │ │ │ ├── zoom-in.cur
│ │ │ │ │ └── zoom-out.cur
│ │ │ │ ├── icon-theme.cache
│ │ │ │ └── index.theme
│ │ │ ├── README.txt
│ │ │ └── hicolor/
│ │ │ └── icon-theme.cache
│ │ ├── main.cc
│ │ ├── science.cadabra.cadabra2-gtk.appdata.xml.in
│ │ ├── theme/
│ │ │ ├── README.txt
│ │ │ └── Windows10/
│ │ │ └── gtk-3.20/
│ │ │ ├── apps/
│ │ │ │ └── gnome-terminal.css
│ │ │ ├── gtk-cadabra.css
│ │ │ ├── gtk-contained-dark.css
│ │ │ ├── gtk-contained.css
│ │ │ ├── gtk-dark.css
│ │ │ ├── gtk.css
│ │ │ └── settings.ini
│ │ └── win_res.rc.in
│ ├── latex/
│ │ ├── install.cmake
│ │ ├── tableaux.sty
│ │ └── young.html
│ ├── osx/
│ │ ├── CMakeLists.txt
│ │ ├── Cadabra/
│ │ │ ├── Cadabra/
│ │ │ │ ├── AppDelegate.h
│ │ │ │ ├── AppDelegate.mm
│ │ │ │ ├── Base.lproj/
│ │ │ │ │ └── Cadabra.xib
│ │ │ │ ├── Images.xcassets/
│ │ │ │ │ └── AppIcon.appiconset/
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Info.plist
│ │ │ │ ├── Notebook.xib
│ │ │ │ ├── NotebookCanvas.hh
│ │ │ │ ├── NotebookCanvas.mm
│ │ │ │ ├── NotebookWindow.hh
│ │ │ │ ├── NotebookWindow.mm
│ │ │ │ ├── Test.xib
│ │ │ │ └── main.m
│ │ │ ├── Cadabra.xcodeproj/
│ │ │ │ ├── project.pbxproj
│ │ │ │ ├── project.xcworkspace/
│ │ │ │ │ ├── contents.xcworkspacedata
│ │ │ │ │ ├── xcshareddata/
│ │ │ │ │ │ └── Cadabra.xccheckout
│ │ │ │ │ └── xcuserdata/
│ │ │ │ │ └── kasper.xcuserdatad/
│ │ │ │ │ └── UserInterfaceState.xcuserstate
│ │ │ │ └── xcuserdata/
│ │ │ │ └── kasper.xcuserdatad/
│ │ │ │ ├── .gitignore
│ │ │ │ └── xcschemes/
│ │ │ │ ├── Cadabra.xcscheme
│ │ │ │ └── xcschememanagement.plist
│ │ │ ├── CadabraTests/
│ │ │ │ ├── CadabraTests.m
│ │ │ │ └── Info.plist
│ │ │ ├── NotebookController.h
│ │ │ └── NotebookController.mm
│ │ └── fake.cc
│ ├── qt5/
│ │ └── README.txt
│ └── web/
│ ├── CMakeLists.txt
│ ├── Makefile
│ ├── README.md
│ ├── css/
│ │ └── cadabra.css
│ ├── docker/
│ │ ├── .gitignore
│ │ └── Dockerfile
│ ├── html/
│ │ └── index.html
│ ├── js/
│ │ └── cadabra.js
│ └── src/
│ ├── NotebookWindow.cc
│ ├── NotebookWindow.hh
│ └── server.py
├── jupyterkernel/
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── cadabra2_jupyter/
│ │ ├── __init__.py.in
│ │ ├── __main__.py
│ │ ├── completer.py
│ │ ├── context.py
│ │ ├── kernel.py
│ │ └── server.py
│ ├── kernelspec/
│ │ └── kernel.json.in
│ ├── lexer/
│ │ ├── cadabra.js
│ │ └── cadabra.py
│ └── readme.txt
├── libs/
│ ├── appdirs/
│ │ └── cdb_appdirs.py
│ ├── base64/
│ │ ├── base64.cc
│ │ └── base64.hh
│ ├── cm/
│ │ ├── cmunbbx.clm1
│ │ ├── cmunbbx.otf
│ │ ├── cmunbi.clm1
│ │ ├── cmunbi.otf
│ │ ├── cmunbl.clm1
│ │ ├── cmunbl.otf
│ │ ├── cmunbmo.clm1
│ │ ├── cmunbmo.otf
│ │ ├── cmunbmr.clm1
│ │ ├── cmunbmr.otf
│ │ ├── cmunbso.clm1
│ │ ├── cmunbso.otf
│ │ ├── cmunbsr.clm1
│ │ ├── cmunbsr.otf
│ │ ├── cmunbtl.clm1
│ │ ├── cmunbtl.otf
│ │ ├── cmunbto.clm1
│ │ ├── cmunbto.otf
│ │ ├── cmunbx.clm1
│ │ ├── cmunbx.otf
│ │ ├── cmunbxo.clm1
│ │ ├── cmunbxo.otf
│ │ ├── cmunci.clm1
│ │ ├── cmunci.otf
│ │ ├── cmunit.clm1
│ │ ├── cmunit.otf
│ │ ├── cmunobi.clm1
│ │ ├── cmunobi.otf
│ │ ├── cmunobx.clm1
│ │ ├── cmunobx.otf
│ │ ├── cmunorm.clm1
│ │ ├── cmunorm.otf
│ │ ├── cmunoti.clm1
│ │ ├── cmunoti.otf
│ │ ├── cmunrb.clm1
│ │ ├── cmunrb.otf
│ │ ├── cmunrm.clm1
│ │ ├── cmunrm.otf
│ │ ├── cmunsi.clm1
│ │ ├── cmunsi.otf
│ │ ├── cmunsl.clm1
│ │ ├── cmunsl.otf
│ │ ├── cmunso.clm1
│ │ ├── cmunso.otf
│ │ ├── cmunss.clm1
│ │ ├── cmunss.otf
│ │ ├── cmunssdc.clm1
│ │ ├── cmunssdc.otf
│ │ ├── cmunst.clm1
│ │ ├── cmunst.otf
│ │ ├── cmunsx.clm1
│ │ ├── cmunsx.otf
│ │ ├── cmuntb.clm1
│ │ ├── cmuntb.otf
│ │ ├── cmunti.clm1
│ │ ├── cmunti.otf
│ │ ├── cmuntt.clm1
│ │ ├── cmuntt.otf
│ │ ├── cmuntx.clm1
│ │ ├── cmuntx.otf
│ │ ├── cmunui.clm1
│ │ ├── cmunui.otf
│ │ ├── cmunvi.clm1
│ │ ├── cmunvi.otf
│ │ ├── cmunvt.clm1
│ │ └── cmunvt.otf
│ ├── dbg/
│ │ └── dbg.h
│ ├── internal/
│ │ └── include/
│ │ └── internal/
│ │ ├── difflib.h
│ │ ├── string_tools.h
│ │ ├── uniconv.h
│ │ ├── unistd.h
│ │ └── uuid.h
│ ├── linenoise/
│ │ ├── LICENSE
│ │ └── linenoise.hpp
│ ├── nlohmann/
│ │ └── nlohmann/
│ │ └── json.hpp
│ ├── pybind11/
│ │ ├── CMakeLists.txt
│ │ ├── LICENSE
│ │ ├── include/
│ │ │ └── pybind11/
│ │ │ ├── attr.h
│ │ │ ├── buffer_info.h
│ │ │ ├── cast.h
│ │ │ ├── chrono.h
│ │ │ ├── common.h
│ │ │ ├── complex.h
│ │ │ ├── detail/
│ │ │ │ ├── class.h
│ │ │ │ ├── common.h
│ │ │ │ ├── cpp_conduit.h
│ │ │ │ ├── descr.h
│ │ │ │ ├── exception_translation.h
│ │ │ │ ├── init.h
│ │ │ │ ├── internals.h
│ │ │ │ ├── type_caster_base.h
│ │ │ │ ├── typeid.h
│ │ │ │ └── value_and_holder.h
│ │ │ ├── eigen/
│ │ │ │ ├── common.h
│ │ │ │ ├── matrix.h
│ │ │ │ └── tensor.h
│ │ │ ├── eigen.h
│ │ │ ├── embed.h
│ │ │ ├── eval.h
│ │ │ ├── functional.h
│ │ │ ├── gil.h
│ │ │ ├── gil_safe_call_once.h
│ │ │ ├── iostream.h
│ │ │ ├── numpy.h
│ │ │ ├── operators.h
│ │ │ ├── options.h
│ │ │ ├── pybind11.h
│ │ │ ├── pytypes.h
│ │ │ ├── stl/
│ │ │ │ └── filesystem.h
│ │ │ ├── stl.h
│ │ │ ├── stl_bind.h
│ │ │ ├── type_caster_pyobject_ptr.h
│ │ │ └── typing.h
│ │ ├── pybind11/
│ │ │ ├── __init__.py
│ │ │ ├── __main__.py
│ │ │ ├── _version.py
│ │ │ ├── commands.py
│ │ │ ├── py.typed
│ │ │ └── setup_helpers.py
│ │ └── tools/
│ │ ├── FindCatch.cmake
│ │ ├── FindEigen3.cmake
│ │ ├── FindPythonLibsNew.cmake
│ │ ├── JoinPaths.cmake
│ │ ├── check-style.sh
│ │ ├── cmake_uninstall.cmake.in
│ │ ├── codespell_ignore_lines_from_errors.py
│ │ ├── libsize.py
│ │ ├── make_changelog.py
│ │ ├── pybind11.pc.in
│ │ ├── pybind11Common.cmake
│ │ ├── pybind11Config.cmake.in
│ │ ├── pybind11GuessPythonExtSuffix.cmake
│ │ ├── pybind11NewTools.cmake
│ │ ├── pybind11Tools.cmake
│ │ ├── pyproject.toml
│ │ ├── setup_global.py.in
│ │ ├── setup_main.py.in
│ │ └── test-pybind11GuessPythonExtSuffix.cmake
│ ├── sqlite3/
│ │ ├── include/
│ │ │ └── sqlite3.h
│ │ └── sqlite3.c
│ ├── tiny-process-library/
│ │ ├── LICENSE
│ │ ├── process.cpp
│ │ ├── process.hpp
│ │ ├── process_unix.cpp
│ │ └── process_win.cpp
│ ├── tinyxml2/
│ │ ├── CMakeLists.txt
│ │ ├── cmake/
│ │ │ ├── tinyxml2-config.cmake
│ │ │ └── tinyxml2.pc.in
│ │ ├── tinyxml2.cpp
│ │ └── tinyxml2.h
│ └── whereami/
│ ├── LICENSE.WTFPLv2
│ ├── whereami.c
│ └── whereami.h
├── man/
│ └── man1/
│ ├── cadabra-server.1
│ ├── cadabra2-cli.1
│ ├── cadabra2-gtk.1
│ ├── cadabra2.1
│ ├── cadabra2cadabra.1
│ ├── cadabra2html.1
│ ├── cadabra2ipynb.1
│ ├── cadabra2latex.1
│ └── cadabra2python.1
├── paper/
│ ├── paper.bib
│ └── paper.md
├── tests/
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── algebra.cdb
│ ├── basic.cdb
│ ├── callbacks.cdb
│ ├── canonicalise.cdb
│ ├── components.cdb
│ ├── decompose.cdb
│ ├── decompose.cnb
│ ├── delta.cdb
│ ├── derivative.cdb
│ ├── display.cdb
│ ├── display.cnb
│ ├── dummies.cdb
│ ├── explicit_implicit.cdb
│ ├── factor.cdb
│ ├── field_theory.cdb
│ ├── fierz.cdb
│ ├── fixed_point.cdb
│ ├── forms.cdb
│ ├── gamma.cdb
│ ├── gamma_paper.cdb
│ ├── implicit.cdb
│ ├── index_positions.cdb
│ ├── integrals.cdb
│ ├── ipynb_module.ipynb
│ ├── kaluza_klein.cdb
│ ├── kerr.cnb
│ ├── latexform.cnb
│ ├── manip.cdb
│ ├── meld.cdb
│ ├── mma.cdb
│ ├── mma.cnb
│ ├── module01.cnb
│ ├── module02.cnb
│ ├── module03.cdb
│ ├── modules.cdb
│ ├── multiterm.cdb
│ ├── nevaluate.cdb
│ ├── new_paper.cdb
│ ├── noncovariant.cdb
│ ├── numerical.cdb
│ ├── output.cdb
│ ├── packages.cdb
│ ├── packages.cnb
│ ├── paper.cdb
│ ├── programming.cdb
│ ├── properties.cdb
│ ├── reduce.cdb
│ ├── relativity.cdb
│ ├── scope.cdb
│ ├── selecting.cdb
│ ├── semicolon-is-display.cnb
│ ├── serialize.cdb
│ ├── simplify.cdb
│ ├── spinors.cdb
│ ├── substitute.cdb
│ ├── symmetry.cdb
│ ├── sympy_bridge.cnb
│ ├── sympy_cdb.cdb
│ ├── test_comparison.cc
│ ├── tests.cdb
│ ├── trigonometric.cdb
│ ├── unicode.cdb
│ ├── vary.cdb
│ ├── working.cdb
│ ├── young.cdb
│ └── yrtrace.cdb
├── tutorials/
│ └── 01_basics.py
├── vcpkg.json
└── web2/
├── CMakeLists.txt
├── README.txt
├── cadabra2/
│ ├── .gitignore
│ ├── robots.txt
│ └── source/
│ ├── blog.html
│ ├── changelog.html
│ ├── changes12.html
│ ├── clay.yaml
│ ├── comparison.html
│ ├── developers.html
│ ├── download.html
│ ├── faq.html
│ ├── features.html
│ ├── help.html
│ ├── index.html
│ ├── jupyter.html
│ ├── layout.html
│ ├── license.html
│ ├── man.html
│ ├── manual/
│ │ ├── .gitignore
│ │ └── README.txt
│ ├── notebook_layout.html
│ ├── notebooks/
│ │ ├── .gitignore
│ │ └── README.txt
│ ├── papers.html
│ ├── people.html
│ ├── quickstart.html
│ ├── static/
│ │ ├── cadabra_in_ipython.nb
│ │ ├── cadabra_in_ipython.nb.html
│ │ ├── fonts/
│ │ │ ├── Bright/
│ │ │ │ ├── OFL-FAQ.txt
│ │ │ │ ├── OFL.txt
│ │ │ │ ├── README.txt
│ │ │ │ └── cmun-bright.css
│ │ │ ├── cmunbx.otf
│ │ │ └── cmunrm.otf
│ │ ├── humans.txt
│ │ ├── images/
│ │ │ └── logo.tex
│ │ ├── js/
│ │ │ └── cadabra.js
│ │ ├── robots.txt
│ │ └── styles/
│ │ ├── cadabra-web.css
│ │ └── normalize.css
│ ├── tutorials.html
│ ├── user_notebooks.html
│ └── v1x.html
└── scan.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*]
end_of_line = lf
insert_final_newline = true
# Matches multiple files with brace expansion notation
[*.{cc,hh}]
charset = utf-8
indent_style = tab
indent_size = 3
trim_trailing_whitespace = true
================================================
FILE: .gitattributes
================================================
*.hh linguist-language=C++
*.cc linguist-language=C++
libs/** linguist-vendored
================================================
FILE: .github/workflows/appimage-modern.yml
================================================
# Modern AppImage build using AppImageBuilder
# Supports both x86_64 and arm64 architectures on current GitHub runners
# Uses Ubuntu 22.04 base for maximum compatibility
name: AppImage (Modern)
on:
release:
types: [created]
# Uncomment for testing
#push:
# branches: [devel]
jobs:
build:
name: AppImage ${{ matrix.arch }}
strategy:
fail-fast: false
matrix:
include:
- arch: x86_64
os: ubuntu-24.04
ubuntu_arch: amd64
appimage_arch: x86_64
repo: http://archive.ubuntu.com/ubuntu/
- arch: arm64
os: ubuntu-24.04-arm
ubuntu_arch: arm64
appimage_arch: aarch64
repo: http://ports.ubuntu.com/ubuntu-ports/
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install AppImageBuilder
run: |
sudo apt update
sudo apt install -y python3-pip
- name: Clean up any existing AppDir
run: |
sudo rm -rf AppDir || true
- name: Create AppImageBuilder recipe
run: |
cat > AppImageBuilder.yml << 'EOF'
version: 1
script:
- mkdir -p AppDir
AppDir:
path: ./AppDir
app_info:
id: science.cadabra.cadabra2-gtk
name: Cadabra2
icon: cadabra2-gtk
version: latest
exec: usr/bin/cadabra2-gtk
exec_args: $@
apt:
arch: ${{ matrix.ubuntu_arch }}
sources:
- sourceline: deb ${{ matrix.repo }} jammy main universe
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x871920D1991BC93C'
- sourceline: deb ${{ matrix.repo }} jammy-updates main universe
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x871920D1991BC93C'
include:
# Core system libraries
- libc6
- libstdc++6
- libgcc-s1
# Shell interpreters
- bash
- dash
- perl-base
# Cadabra dependencies
- libgmp10
- libgmpxx4ldbl
- libboost-system1.74.0
- libboost-filesystem1.74.0
- libboost-program-options1.74.0
- libboost-date-time1.74.0
- libsqlite3-0
- uuid-runtime
- libssl3
- libtbb12
# GTK and GUI dependencies
- libgtkmm-3.0-1v5
- libgtk-3-0
- libgdk-pixbuf2.0-0
- libcairo2
- libpango-1.0-0
- libpangocairo-1.0-0
- libatk1.0-0
- libglib2.0-0
- librsvg2-common
- adwaita-icon-theme
- hicolor-icon-theme
- libwayland-client0
- libwayland-cursor0
- libwayland-egl1-mesa
# Python dependencies (Ubuntu 22.04 uses Python 3.10)
- python3.10
- libpython3.10
- python3-pip
- python3-gmpy2
exclude:
- adwaita-icon-theme-full
- humanity-icon-theme
- ubuntu-mono
files:
exclude:
- usr/lib/python*/site-packages/pip*
- usr/lib/python*/site-packages/setuptools*
- usr/share/doc
- usr/share/man
- usr/share/locale
- var/cache
- var/lib/apt
- etc/apt
runtime:
env:
PATH: '${APPDIR}/usr/bin:${PATH}'
PYTHONHOME: '${APPDIR}/usr'
PYTHONPATH: '${APPDIR}/usr/lib/python3.10/site-packages:${APPDIR}/usr/lib/python3.10:${APPDIR}/usr/lib/python3.10/dist-packages'
LD_LIBRARY_PATH: '${APPDIR}/usr/lib:${APPDIR}/usr/lib/${{ matrix.arch }}-linux-gnu:${LD_LIBRARY_PATH}'
LC_ALL: C.UTF-8
LANG: C.UTF-8
AppImage:
update-information: gh-releases-zsync|kpeeters|cadabra2|latest|Cadabra*${{ matrix.arch }}.AppImage.zsync
sign-key: None
arch: ${{ matrix.appimage_arch }}
comp: gzip
EOF
- name: Build Cadabra2 in container
run: |
# Use Docker to build for the target architecture with Ubuntu 22.04
docker run --rm --privileged \
--platform linux/${{ matrix.arch }} \
-v $PWD:/workspace \
-w /workspace \
ubuntu:22.04 /bin/bash -c "
# Stop on error inside the Docker container
set -e
# Install dependencies
apt update
DEBIAN_FRONTEND=noninteractive apt install -y \
build-essential cmake git \
python3-dev python3-pip g++ \
libgmp3-dev libgtkmm-3.0-dev \
libboost-all-dev libssl-dev \
libsqlite3-dev uuid-dev \
python3-matplotlib python3-sympy \
python3-gmpy2 python3-numpy \
squashfs-tools file desktop-file-utils fakeroot strace patchelf zsync
# Install directly from the main branch of the appimage-builder repository,
# to deal with the issue reported in
# https://github.com/AppImageCrafters/appimage-builder/pull/281
pip3 install git+https://github.com/AppImageCrafters/appimage-builder.git@main
# Patch mpmath (bug fixed only in ubuntu-22.04)
# sed -i \"s/if other is 0:/if other == 0:/g\" /usr/lib/python3/dist-packages/mpmath/ctx_mp_python.py
# cat /usr/lib/python3/dist-packages/mpmath/ctx_mp_python.py
# Build Cadabra2
git config --global --add safe.directory /workspace
mkdir -p build
cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_MATHEMATICA=OFF \
-DAPPIMAGE_MODE=ON \
..
VERSION=\$(cat /workspace/build/VERSION)
echo \"Building version \${VERSION}\"
make -j\$(nproc)
make install DESTDIR=/workspace/build/AppDir
make test
# Install Python packages directly into the AppDir after 'make install'
# Ensure the target path is correct for your AppDir's Python installation
pip3 install --target=/workspace/build/AppDir/usr/lib/python3.10/site-packages --upgrade mpmath>=1.2.0
pip3 install --target=/workspace/build/AppDir/usr/lib/python3.10/site-packages astunparse
pip3 install --target=/workspace/build/AppDir/usr/lib/python3.10/site-packages pillow sympy matplotlib numpy
# Update version in AppImageBuilder.yml
sed -i \"s/version: latest/version: \${VERSION}/\" /workspace/AppImageBuilder.yml
# Build the AppImage
echo \"Now going to run appimage-builder, using:\"
echo \"---------\"
cat /workspace/AppImageBuilder.yml
echo \"---------\"
echo \"Here we go...\"
appimage-builder --recipe /workspace/AppImageBuilder.yml --skip-test
echo \"This is /workspace/build/AppDir/usr/bin/ :\"
echo \"---------\"
ls -la /workspace/build/AppDir/usr/bin/
echo \"---------\"
"
- name: Rename AppImage
run: |
VERSION=`cat build/VERSION`
sudo chown runner:docker build -R
# Find and rename the AppImage
ls -la .
APPIMAGE_FILE=$(find . -name "*.AppImage" -type f | head -n 1)
if [ -n "$APPIMAGE_FILE" ]; then
mv "$APPIMAGE_FILE" "Cadabra_${VERSION}_${{ matrix.arch }}.AppImage"
chmod +x "Cadabra_${VERSION}_${{ matrix.arch }}.AppImage"
fi
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: cadabra2-appimage-${{ matrix.arch }}
path: Cadabra_*_${{ matrix.arch }}.AppImage
retention-days: 7
- name: Upload to release
if: github.event_name == 'release'
run: |
gh release upload "${{ github.ref_name }}" Cadabra_*_${{ matrix.arch }}.AppImage --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# - name: Generate zsync file
# if: github.event_name == 'release'
# run: |
# # Install zsync
# sudo apt install -y zsync
#
# # Generate zsync file for the AppImage
# APPIMAGE_FILE=$(find . -name "Cadabra_*_${{ matrix.arch }}.AppImage" -type f | head -n 1)
# if [ -n \"$APPIMAGE_FILE\" ]; then
# zsyncmake \"$APPIMAGE_FILE\"
#
# # Upload zsync file to release
# gh release upload ${{ github.ref_name }} *.zsync --clobber || true
# fi
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/c++lib.yml
================================================
# This is a build which gets triggered on every commit push, to
# ensure that Cadabra builds as c++lib. Does not yet contain any
# tests, it just checks for build issues.
name: c++lib
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: get dependencies
run: sudo apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get install git cmake python3-dev g++ libpcre3 libpcre3-dev libgmp3-dev libboost-all-dev libgmp-dev libsqlite3-dev uuid-dev libmpfr-dev libmpc-dev
- name: configure
run: mkdir build-lib && cd build-lib && cmake -DBUILD_AS_CPP_LIBRARY=ON ..
- name: make
run: cd build-lib && make
================================================
FILE: .github/workflows/docker.yml
================================================
# Build docker images with a Jupyter server with Cadabra
# kernel whenever a commit is pushed to github.
#
# Lint this thing with
#
# yq eval docker.yml
name: Docker
# on: [push]
on:
release:
types: [released]
jobs:
build:
runs-on: ubuntu-latest
# strategy:
# fail-fast: false
# matrix:
# arch:
# - amd64
# - arm64
steps:
- name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Set up QEMU
uses: docker/setup-qemu-action@master
- uses: actions/checkout@v3
# - name: Exit if not on master branch
# if: github.ref != 'refs/heads/master'
# run: exit 1
- name: Log in to Docker Hub
uses: docker/login-action@master
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@master
with:
images: kpeeters/cadabra2-jupyter
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@master
- name: Build Docker image
uses: docker/build-push-action@master
with:
platforms: linux/amd64, linux/arm64
context: .
file: docker/Dockerfile
push: true
#${{ github.ref == 'master' }}
labels: ${{ steps.meta.outputs.labels }}
tags: kpeeters/cadabra2-jupyter:latest
# outputs: type=docker
# - name: Upload artifact to github assets
# uses: actions/upload-artifact@main
# with:
# name: cadabra2-jupyter-${{ matrix.arch }}
# path: cadabra2-jupyter-${{ matrix.arch }}.tar
================================================
FILE: .github/workflows/fedora-40-package.yml
================================================
# Create a Fedora 40 package on a github release event.
# This assumes that the cadabra version is the same as the
# release name, and it will attempt to add the .rpm file
# to the release assets.
name: Fedora-40 package
# on: [push]
on:
release:
types: [created]
jobs:
build:
strategy:
matrix:
include:
- os: ubuntu-24.04
name: x86_64
display-name: "Fedora 40 x86_64"
- os: ubuntu-24.04-arm
name: arm64
display-name: "Fedora 40 arm64"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@master
- name: Pull Fedora image
run: docker pull fedora:40
- name: Set up GitHub CLI
run: |
sudo apt-get update
sudo apt-get install -y gh
- name: Authenticate GitHub CLI
run: gh auth setup-git
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build RPM in Fedora container
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
fedora:40 \
bash -c "
git config --global --add safe.directory /workspace
dnf install -y rpm-build make gcc-c++ git python3-devel cmake gmp-devel libuuid-devel sqlite-devel openssl-devel gtkmm30-devel boost-devel python3-matplotlib python3-pip
pip3 install sympy
mkdir build
cd build
cmake -DPACKAGING_MODE=ON -DENABLE_MATHEMATICA=OFF -DCMAKE_INSTALL_PREFIX=/usr ..
make
cpack
"
- name: Set version variables from output of cmake
run: |
VER=$(cat ${{ github.workspace }}/build/VERSION)
echo "VERSION=$VER" >> $GITHUB_ENV
- name: Upload Release Assets
run: |
gh release upload "${{ env.VERSION }}" build/cadabra2-${{ env.VERSION }}-fedora40-${{ matrix.name }}.rpm --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
test:
needs: build
runs-on: ubuntu-24.04
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@master
- name: Pull Fedora image
run: docker pull fedora:40
- name: Download package and run inside Fedora container
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
fedora:40 \
bash -c "
dnf install -y xorg-x11-server-Xvfb wget curl jq
export VERSION=\$(curl -s https://api.github.com/repos/kpeeters/cadabra2/releases|& jq .[0].tag_name -r)
export RPMNAME=cadabra2-\${VERSION}-fedora40-x86_64.rpm
wget https://github.com/kpeeters/cadabra2/releases/download/\${VERSION}/\${RPMNAME}
dnf install -y \${RPMNAME}
printf 'import sys\nprint(sys.path)\nimport cdb.main\nex:=(A+B) (C+D);\ndistribute(ex);\nquit()\n' > tst.cdb
cadabra2 tst.cdb
xvfb-run -a cadabra2-gtk &
APP_PID=\$!
sleep 10
if kill -0 \$APP_PID 2>/dev/null; then
echo 'cadabra2-gtk started successfully'
kill \$APP_PID
exit 0
else
echo 'cadabra2-gtk failed to start'
exit 1
fi
"
================================================
FILE: .github/workflows/fedora-41-package.yml
================================================
# Create a Fedora 41 package on a github release event.
# This assumes that the cadabra version is the same as the
# release name, and it will attempt to add the .rpm file
# to the release assets.
name: Fedora-41 package
# on: [push]
on:
release:
types: [created]
jobs:
build:
strategy:
matrix:
include:
- os: ubuntu-24.04
name: x86_64
display-name: "Fedora 41 x86_64"
- os: ubuntu-24.04-arm
name: arm64
display-name: "Fedora 41 arm64"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@master
- name: Pull Fedora image
run: docker pull fedora:41
- name: Set up GitHub CLI
run: |
sudo apt-get update
sudo apt-get install -y gh
- name: Authenticate GitHub CLI
run: gh auth setup-git
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build RPM in Fedora container
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
fedora:41 \
bash -c "
git config --global --add safe.directory /workspace
dnf install -y rpm-build make gcc-c++ git python3-devel cmake gmp-devel libuuid-devel sqlite-devel openssl-devel gtkmm30-devel boost-devel python3-matplotlib python3-pip
pip3 install sympy
mkdir build
cd build
cmake -DPACKAGING_MODE=ON -DENABLE_MATHEMATICA=OFF -DCMAKE_INSTALL_PREFIX=/usr ..
make
cpack
"
- name: Set version variables from output of cmake
run: |
VER=$(cat ${{ github.workspace }}/build/VERSION)
echo "VERSION=$VER" >> $GITHUB_ENV
- name: Upload Release Assets
run: |
gh release upload "${{ env.VERSION }}" build/cadabra2-${{ env.VERSION }}-fedora41-${{ matrix.name }}.rpm --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
test:
needs: build
runs-on: ubuntu-24.04
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@master
- name: Pull Fedora image
run: docker pull fedora:41
- name: Download package and run inside Fedora container
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
fedora:41 \
bash -c "
dnf install -y xorg-x11-server-Xvfb wget curl jq
export VERSION=\$(curl -s https://api.github.com/repos/kpeeters/cadabra2/releases|& jq .[0].tag_name -r)
export RPMNAME=cadabra2-\${VERSION}-fedora41-x86_64.rpm
wget https://github.com/kpeeters/cadabra2/releases/download/\${VERSION}/\${RPMNAME}
dnf install -y \${RPMNAME}
printf 'import sys\nprint(sys.path)\nimport cdb.main\nex:=(A+B) (C+D);\ndistribute(ex);\nquit()\n' > tst.cdb
cadabra2 tst.cdb
xvfb-run -a cadabra2-gtk &
APP_PID=\$!
sleep 10
if kill -0 \$APP_PID 2>/dev/null; then
echo 'cadabra2-gtk started successfully'
kill \$APP_PID
exit 0
else
echo 'cadabra2-gtk failed to start'
exit 1
fi
"
================================================
FILE: .github/workflows/fedora-42-package.yml
================================================
# Create a Fedora 41 package on a github release event.
# This assumes that the cadabra version is the same as the
# release name, and it will attempt to add the .rpm file
# to the release assets.
name: Fedora-42 package
# on: [push]
on:
release:
types: [created]
jobs:
build:
strategy:
matrix:
include:
- os: ubuntu-24.04
name: x86_64
display-name: "Fedora 42 x86_64"
- os: ubuntu-24.04-arm
name: arm64
display-name: "Fedora 42 arm64"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@master
- name: Pull Fedora image
run: docker pull fedora:42
- name: Set up GitHub CLI
run: |
sudo apt-get update
sudo apt-get install -y gh
- name: Authenticate GitHub CLI
run: gh auth setup-git
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build RPM in Fedora container
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
fedora:42 \
bash -c "
git config --global --add safe.directory /workspace
dnf install -y rpm-build make gcc-c++ git python3-devel cmake gmp-devel libuuid-devel sqlite-devel openssl-devel gtkmm30-devel boost-devel python3-matplotlib python3-pip
pip3 install sympy
mkdir build
cd build
cmake -DPACKAGING_MODE=ON -DENABLE_MATHEMATICA=OFF -DCMAKE_INSTALL_PREFIX=/usr ..
make
cpack
"
- name: Set version variables from output of cmake
run: |
VER=$(cat ${{ github.workspace }}/build/VERSION)
echo "VERSION=$VER" >> $GITHUB_ENV
- name: Upload Release Assets
if: github.event_name == 'release'
run: |
gh release upload "${{ env.VERSION }}" build/cadabra2-${{ env.VERSION }}-fedora42-${{ matrix.name }}.rpm --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Store package as build artifact
uses: actions/upload-artifact@v4
if: github.event_name == 'push'
with:
name: cadabra2-fedora42-${{ matrix.name }}.rpm
path: build/cadabra2-${{ env.VERSION }}-fedora42-${{ matrix.name }}.rpm
test:
needs: build
runs-on: ubuntu-latest
if: github.event_name == 'release' # only test release builds
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@master
- name: Pull Fedora image
run: docker pull fedora:42
- name: Download package and run inside Fedora container
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
fedora:42 \
bash -c "
dnf install -y xorg-x11-server-Xvfb wget curl jq
export VERSION=\$(curl -s https://api.github.com/repos/kpeeters/cadabra2/releases|& jq .[0].tag_name -r)
export RPMNAME=cadabra2-\${VERSION}-fedora42-x86_64.rpm
wget https://github.com/kpeeters/cadabra2/releases/download/\${VERSION}/\${RPMNAME}
dnf install -y \${RPMNAME}
printf 'import sys\nprint(sys.path)\nimport cdb.main\nex:=(A+B) (C+D);\ndistribute(ex);\nquit()\n' > tst.cdb
cadabra2 tst.cdb
xvfb-run -a cadabra2-gtk &
APP_PID=\$!
sleep 10
if kill -0 \$APP_PID 2>/dev/null; then
echo 'cadabra2-gtk started successfully'
kill \$APP_PID
exit 0
else
echo 'cadabra2-gtk failed to start'
exit 1
fi
"
================================================
FILE: .github/workflows/freebsd.yml
================================================
# This is a build which gets triggered on every commit push, to
# ensure that we get some warnings when we push code that does
# not build on Linux.
name: FreeBSD
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build and test in FreeBSD
id: test
uses: vmactions/freebsd-vm@v1
with:
usesh: true
prepare: |
pkg install -y curl cmake python311 gettext-runtime pkgconf boost-libs
pkg install -y fontconfig harfbuzz pcre jsoncpp gmp atkmm cairomm glibmm gtkmm30 pangomm
pkg install -y git py311-sympy py311-numpy
run: |
pwd
env
freebsd-version
sysctl hw.model
sysctl hw.ncpu
sysctl hw.physmem
sysctl hw.usermem
mkdir build && cd build && cmake -DENABLE_MATHEMATICA=OFF ..
make
make install
make check
================================================
FILE: .github/workflows/homebrew-devel.yml
================================================
# This is a build which gets triggered on every push to the
# 'devel' branch, to update the homebrew-repo the cadabra2-devel
# package.
name: Homebrew-devel
on:
push:
branches:
- devel
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Cadabra
uses: actions/checkout@v4
with:
ref: 'devel'
- name: Checkout Homebrew repo
uses: actions/checkout@v4
with:
repository: kpeeters/homebrew-repo
path: homebrew-repo
token: ${{ secrets.ACTIONS_HOMEBREW_REPO_TOKEN }}
- name: Set up GitHub CLI
run: |
sudo apt-get update
sudo apt-get install -y gh
- name: Authenticate GitHub CLI
run: gh auth setup-git
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get dependencies
run: sudo apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get install git cmake jq python3-dev g++ libpcre3 libpcre3-dev libgmp3-dev libgtkmm-3.0-dev libboost-all-dev libgmp-dev libsqlite3-dev uuid-dev libmpfr-dev libmpc-dev
- name: Configure
run: mkdir build && cd build && cmake -DPACKAGING_MODE=ON -DENABLE_MATHEMATICA=OFF -DCMAKE_INSTALL_PREFIX=/usr ..
- name: Set variables
run: |
export VERSION=$(cat build/VERSION)
export COMMIT=$(curl -s https://api.github.com/repos/kpeeters/cadabra2/commits/devel | jq -r .sha)
wget https://github.com/kpeeters/cadabra2/archive/${COMMIT}.tar.gz
export SHA=`cat ${COMMIT}.tar.gz | sha256sum -b | cut -d " " -f 1`
echo "SHA=${SHA}" >> $GITHUB_ENV
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "COMMIT=$COMMIT" >> $GITHUB_ENV
- name: Update Homebrew repo
run: |
cd homebrew-repo
cat cadabra2-devel.rb | sed -e 's/^ url .*/ url "https:\/\/github.com\/kpeeters\/cadabra2\/archive\/${{ env.COMMIT }}.tar.gz"/' | sed -e 's/^ sha256.*/ sha256 "${{ env.SHA }}"/' | sed -e 's/^ version.*/ version "${{ env.VERSION }}.${{ env.COMMIT }}"/' > out.rb
mv out.rb cadabra2-devel.rb
cat cadabra2-devel.rb
git config user.name "Kasper Peeters"
git config user.email "info@cadabra.science"
git add cadabra2-devel.rb
git diff-index --quiet HEAD || (git commit -a -m "Update to release ${{ env.VERSION }} commit ${{ env.COMMIT }}." && git push)
test:
needs: build
runs-on: macos-latest
steps:
- name: get dependencies
run: brew update && brew upgrade && brew tap kpeeters/repo && brew install --verbose cadabra2-devel
- name: show where cadabra is located
run: cat `which cadabra2`
- name: test simple cli run
run: printf "import sys\nprint(sys.path)\nimport cdb.main\nex:=(A+B) (C+D);\ndistribute(ex);\nquit()\n" > tst.cdb && cadabra2 tst.cdb
- name: test gtk app launch
run: |
cadabra2-gtk &
APP_PID=$!
sleep 10
if kill -0 $APP_PID 2>/dev/null; then
echo "cadabra2-gtk started successfully"
kill $APP_PID
exit 0
else
echo "cadabra2-gtk failed to start"
exit 1
fi
- name: Upload build logs
if: always()
uses: actions/upload-artifact@v4
with:
name: brew-build-logs
path: |
~/Library/Logs/Homebrew/
retention-days: 2
================================================
FILE: .github/workflows/homebrew.yml
================================================
# This is a build which gets triggered on every release, to
# update the homebrew-repo.
name: Homebrew
on:
release:
types: [released]
# on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Cadabra
uses: actions/checkout@v4
- name: Checkout Homebrew repo
uses: actions/checkout@v4
with:
repository: kpeeters/homebrew-repo
path: homebrew-repo
token: ${{ secrets.ACTIONS_HOMEBREW_REPO_TOKEN }}
- name: Set up GitHub CLI
run: |
sudo apt-get update
sudo apt-get install -y gh
- name: Authenticate GitHub CLI
run: gh auth setup-git
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get dependencies
run: sudo apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get install git cmake jq python3-dev g++ libpcre3 libpcre3-dev libgmp3-dev libgtkmm-3.0-dev libboost-all-dev libgmp-dev libsqlite3-dev uuid-dev libmpfr-dev libmpc-dev
- name: Configure
run: mkdir build && cd build && cmake -DPACKAGING_MODE=ON -DENABLE_MATHEMATICA=OFF -DCMAKE_INSTALL_PREFIX=/usr ..
- name: Set variables
run: |
export VERSION=$(cat build/VERSION)
echo "VERSION=$VERSION" >> $GITHUB_ENV
wget https://github.com/kpeeters/cadabra2/archive/refs/tags/${VERSION}.tar.gz
export SHA=`cat ${VERSION}.tar.gz | sha256sum -b | cut -d " " -f 1`
echo "SHA=${SHA}" >> $GITHUB_ENV
- name: Update Homebrew repo
run: |
cd homebrew-repo
cat cadabra2.rb | sed -e 's/^ url .*/ url "https:\/\/github.com\/kpeeters\/cadabra2\/archive\/refs\/tags\/${{ env.VERSION }}.tar.gz"/' | sed -e 's/^ sha256.*/ sha256 "${{ env.SHA }}"/' | sed -e 's/^ version.*/ version "${{ env.VERSION }}"/' > out.rb
mv out.rb cadabra2.rb
cat cadabra2.rb
git config user.name "Kasper Peeters"
git config user.email "info@cadabra.science"
git add cadabra2.rb
git diff-index --quiet HEAD || (git commit -a -m "Update to release ${{ env.VERSION }}." && git push)
test:
needs: build
runs-on: macos-latest
steps:
- name: get dependencies
run: brew update && brew upgrade && brew tap kpeeters/repo && brew install cadabra2
- name: show where cadabra is located
run: cat `which cadabra2`
- name: test simple cli run
run: printf "ex:=(A+B) (C+D);\ndistribute(ex);\nquit()\n" > tst.cdb && cadabra2 tst.cdb
- name: test gtk app launch
run: |
cadabra2-gtk &
APP_PID=$!
sleep 10
if kill -0 $APP_PID 2>/dev/null; then
echo "Application started successfully"
kill $APP_PID
exit 0
else
echo "Application failed to start"
exit 1
fi
================================================
FILE: .github/workflows/linux.yml
================================================
# This is a build which gets triggered on every commit push, to
# ensure that we get some warnings when we push code that does
# not build on Linux.
name: Linux
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# - name: Exit if not on devel branch
# if: github.ref != 'refs/heads/devel'
# run: exit 1
# - name: setup python
# uses: actions/setup-python@v4
# with:
# python-version: 3.11
- name: get dependencies
run: sudo apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get install ninja-build git cmake python3-dev g++ libpcre3 libpcre3-dev libgmp3-dev libgtkmm-3.0-dev libboost-all-dev libgmp-dev libsqlite3-dev uuid-dev libmpfr-dev libmpc-dev libtbb-dev catch2 && python3 --version && which python3 && python3 -m pip install --upgrade pip && python3 -m pip install wheel && python3 -m pip install sympy gmpy2 numpy
- name: configure
run: mkdir build && cd build && cmake -G Ninja -DENABLE_MATHEMATICA=OFF ..
- name: build
run: cd build && cmake --build .
- name: test
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: cd build && ctest ARGS="-V"
================================================
FILE: .github/workflows/macos.yml
================================================
# This is a build which gets triggered on every commit push, to
# ensure that we get some warnings when we push code that does
# not build on macOS.
name: macOS
on: [push]
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
# - name: Exit if not on devel branch
# if: github.ref != 'refs/heads/devel'
# run: exit 1
# - name: remove python 3.12
# run: brew uninstall python@3.12
- name: get dependencies
run: brew install cmake ninja boost pcre gmp python@3.13 pkgconfig gtkmm3 adwaita-icon-theme catch2 tbb && python3 -m pip install --break-system-packages --user sympy gmpy2 numpy
- name: configure
run: mkdir build && cd build && cmake -G Ninja -DENABLE_MATHEMATICA=OFF -DPython_EXECUTABLE=python3.12 ..
- name: build
run: cd build && cmake --build .
- name: test
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: cd build && ctest ARGS="-V"
================================================
FILE: .github/workflows/opensuse-tumbleweed-package.yml
================================================
# Create a OpenSUSE Tumbleweed package on a github release event.
# This assumes that the cadabra version is the same as the
# release name, and it will attempt to add the .rpm file
# to the release assets.
name: OpenSUSE-Tumbleweed package
# on: [push]
on:
release:
types: [created]
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
# - name: Exit if not on devel branch
# if: github.ref != 'refs/heads/devel'
# run: exit 1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@master
- name: Pull OpenSUSE image
run: docker pull opensuse/tumbleweed:latest
- name: Set up GitHub CLI
run: |
sudo apt-get update
sudo apt-get install -y gh
- name: Authenticate GitHub CLI
run: gh auth setup-git
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build RPM
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
opensuse/tumbleweed:latest \
bash -c "
zypper refresh
zypper update
zypper -n install --no-recommends rpmbuild git cmake python313-devel gcc-c++ \
gmp-devel libuuid-devel \
gtkmm3-devel sqlite3-devel \
python313-matplotlib libopenssl-devel \
libboost_system-devel libboost_filesystem-devel \
libboost_date_time-devel libboost_program_options-devel
git config --global --add safe.directory /workspace
mkdir build
cd build
cmake -DPACKAGING_MODE=ON -DENABLE_MATHEMATICA=OFF -DCMAKE_INSTALL_PREFIX=/usr ..
make
cpack
"
- name: Set version variables from output of cmake
run: |
VER=$(cat ${{ github.workspace }}/build/VERSION)
echo "VERSION=$VER" >> $GITHUB_ENV
- name: Upload Release Assets
run: |
gh release upload "${{ env.VERSION }}" build/cadabra2-${{ env.VERSION }}-tumbleweed.rpm --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
test:
needs: build
runs-on: ubuntu-24.04
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@master
- name: Pull OpenSUSE image
run: docker pull opensuse/tumbleweed:latest
- name: Download package and run inside OpenSUSE container
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
opensuse/tumbleweed:latest \
bash -c "
zypper clean --all
zypper refresh
zypper update
zypper se -s libboost_filesystem
zypper -n install xvfb-run wget curl jq
export VERSION=\$(curl -s https://api.github.com/repos/kpeeters/cadabra2/releases|& jq .[0].tag_name -r)
export RPMNAME=cadabra2-\${VERSION}-tumbleweed.rpm
wget https://github.com/kpeeters/cadabra2/releases/download/\${VERSION}/\${RPMNAME}
zypper --no-gpg-checks -n install \${RPMNAME}
printf 'import sys\nprint(sys.path)\nimport cdb.main\nex:=(A+B) (C+D);\ndistribute(ex);\nquit()\n' > tst.cdb
cadabra2 tst.cdb
xvfb-run -a cadabra2-gtk &
APP_PID=\$!
sleep 10
if kill -0 \$APP_PID 2>/dev/null; then
echo 'cadabra2-gtk started successfully'
kill \$APP_PID
exit 0
else
echo 'cadabra2-gtk failed to start'
exit 1
fi
"
================================================
FILE: .github/workflows/tarball.yml
================================================
# This is a build which gets triggered on every release, to
# generate a tarball which includes all submodules.
name: Tarball
on:
release:
types: [released]
# on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Cadabra
uses: actions/checkout@v4
- name: Set up GitHub CLI
run: |
sudo apt-get update
sudo apt-get install -y gh
- name: Authenticate GitHub CLI
run: gh auth setup-git
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create tarball
run: |
export CDB_MAJOR=`cat cmake/version.cmake |grep 'MAJOR '|sed -e 's/[^ ]* \([0-9]*\).*/\1/'`
export CDB_MINOR=`cat cmake/version.cmake |grep 'MINOR '|sed -e 's/[^ ]* \([0-9]*\).*/\1/'`
export CDB_PATCH=`cat cmake/version.cmake |grep 'PATCH '|sed -e 's/[^ ]* \([0-9]*\).*/\1/'`
export VERSION=${CDB_MAJOR}.${CDB_MINOR}.${CDB_PATCH}
echo "VERSION=$VERSION" >> $GITHUB_ENV
mkdir -p submodules
cd submodules
git clone -b kpeeters/cadabra https://github.com/kpeeters/MicroTeX.git microtex
cd ../..
mv cadabra2 cadabra2-${VERSION}
tar zcf cadabra2-${VERSION}-source-inclusive.tar.gz --exclude ".git" cadabra2-${VERSION}
mv cadabra2-${VERSION} cadabra2
- name: Upload tarball to assets
run: |
gh release upload "${{ env.VERSION }}" ${{ github.workspace }}/../cadabra2-${VERSION}-source-inclusive.tar.gz --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/ubuntu-22.04-package.yml
================================================
# Create an Ubuntu 22.04 package on a github release event.
# This assumes that the cadabra version is the same as the
# release name, and it will attempt to add the .deb file
# to the release assets.
name: Ubuntu-22.04 package
# on: [push]
on:
release:
types: [created]
jobs:
build:
strategy:
matrix:
include:
- os: ubuntu-22.04
name: x86_64
display-name: "Ubuntu 22.04 x86_64"
- os: ubuntu-22.04-arm
name: arm64
display-name: "Ubuntu 22.04 arm64"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
# - name: Exit if not on devel branch
# if: github.ref != 'refs/heads/devel'
# run: exit 1
- name: Set up GitHub CLI
run: |
sudo apt-get update
sudo apt-get install -y gh
- name: Authenticate GitHub CLI
run: gh auth setup-git
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get dependencies
run: sudo apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get install git cmake ninja-build python3-dev g++ libpcre3 libpcre3-dev libgmp3-dev libgtkmm-3.0-dev libboost-all-dev libgmp-dev libsqlite3-dev uuid-dev libmpfr-dev libmpc-dev libtbb-dev catch2 && python3 --version && which python3 && python3 -m pip install --upgrade pip && python3 -m pip install wheel && python3 -m pip install sympy gmpy2 numpy
- name: Configure
run: mkdir build && cd build && cmake -G Ninja -DPACKAGING_MODE=ON -DENABLE_MATHEMATICA=OFF -DCMAKE_INSTALL_PREFIX=/usr ..
- name: Build
run: cd build && cmake --build .
- name: Create the .deb package
run: cd build && cpack
- name: Set version variables from output of cmake
run: |
VER=$(cat build/VERSION)
echo "VERSION=$VER" >> $GITHUB_ENV
- name: Upload Release Assets
run: |
gh release upload "${{ env.VERSION }}" build/cadabra2-${{ env.VERSION }}-ubuntu-22.04-jammy-${{ matrix.name }}.deb --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
test:
needs: build
runs-on: ubuntu-22.04
steps:
- name: Get and install package
run: |
sudo apt install xvfb
export VERSION=$(curl -s https://api.github.com/repos/kpeeters/cadabra2/releases|& jq .[0].tag_name -r)
export DEBNAME=cadabra2-${VERSION}-ubuntu-22.04-jammy-x86_64.deb
wget https://github.com/kpeeters/cadabra2/releases/download/${VERSION}/${DEBNAME}
sudo apt -y update
sudo apt -y upgrade
sudo apt -y install ./${DEBNAME}
- name: Test simple cli run
run: printf "import sys\nprint(sys.path)\nimport cdb.main\nex:=(A+B) (C+D);\ndistribute(ex);\nquit()\n" > tst.cdb && cadabra2 tst.cdb
- name: Test gtk app launch
run: |
xvfb-run -a cadabra2-gtk &
APP_PID=$!
sleep 10
if kill -0 $APP_PID 2>/dev/null; then
echo "Application started successfully"
kill $APP_PID
exit 0
else
echo "Application failed to start"
exit 1
fi
================================================
FILE: .github/workflows/ubuntu-24.04-package.yml
================================================
# Create an Ubuntu 24.04 package on a github release event.
# This assumes that the cadabra version is the same as the
# release name, and it will attempt to add the .deb file
# to the release assets.
name: Ubuntu-24.04 package
# on: [push]
on:
release:
types: [created]
jobs:
build:
strategy:
matrix:
include:
- os: ubuntu-24.04
name: x86_64
display-name: "Ubuntu 24.04 x86_64"
- os: ubuntu-24.04-arm
name: arm64
display-name: "Ubuntu 24.04 arm64"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
# - name: Exit if not on devel branch
# if: github.ref != 'refs/heads/devel'
# run: exit 1
- name: Set up GitHub CLI
run: |
sudo apt-get update
sudo apt-get install -y gh
- name: Authenticate GitHub CLI
run: gh auth setup-git
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get dependencies
run: sudo apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get install git cmake ninja-build python3-dev g++ libpcre3 libpcre3-dev libgmp3-dev libgtkmm-3.0-dev libboost-all-dev libgmp-dev libsqlite3-dev uuid-dev libmpfr-dev libmpc-dev libtbb-dev catch2 && python3 --version && which python3 && python3 -m pip install --upgrade pip && python3 -m pip install wheel && python3 -m pip install sympy gmpy2 numpy
- name: configure
run: mkdir build && cd build && cmake -G Ninja -DPACKAGING_MODE=ON -DENABLE_MATHEMATICA=OFF -DCMAKE_INSTALL_PREFIX=/usr ..
- name: Make
run: cd build && cmake --build .
- name: Create the .deb package
run: cd build && cpack
- name: Set version variables from output of cmake
run: |
VER=$(cat build/VERSION)
echo "VERSION=$VER" >> $GITHUB_ENV
- name: Upload Release Assets
if: github.event_name == 'release'
run: |
gh release upload "${{ env.VERSION }}" build/cadabra2-${{ env.VERSION }}-ubuntu-24.04-noble-${{ matrix.name }}.deb --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Store package as build artifact
uses: actions/upload-artifact@v4
if: github.event_name == 'push'
with:
name: cadabra2-ubuntu-24.04-noble-${{ matrix.name }}.deb
path: build/cadabra2-${{ env.VERSION }}-ubuntu-24.04-noble.deb
test:
needs: build
runs-on: ubuntu-24.04
steps:
- name: Install prerequisites for testing
run: |
sudo apt install xvfb imagemagick
sudo apt-mark hold firefox
- name: Get and install package (release mode)
if: github.event_name == 'release'
run: |
export VERSION=$(curl -s https://api.github.com/repos/kpeeters/cadabra2/releases|& jq .[0].tag_name -r)
export DEBNAME=cadabra2-${VERSION}-ubuntu-24.04-noble-x86_64.deb
wget https://github.com/kpeeters/cadabra2/releases/download/${VERSION}/${DEBNAME}
sudo apt -y update
sudo apt -y upgrade
sudo apt -y install ./${DEBNAME}
- name: Download package (push mode)
uses: actions/download-artifact@v4
if: github.event_name == 'push'
with:
name: cadabra2-ubuntu-24.04-noble-x86_64.deb
- name: Install package (push mode)
if: github.event_name == 'push'
run: |
ls -la
sudo apt -y update
sudo apt -y upgrade
sudo apt -y install ./cadabra2*.deb
- name: Test simple cli run
run: printf "import sys\nprint(sys.path)\nimport cdb.main\nex:=(A+B) (C+D);\ndistribute(ex);\nquit()\n" > tst.cdb && cadabra2 tst.cdb
- name: Test gtk app launch and take screenshot
run: |
MESA_LOADER_DRIVER_OVERRIDE=llvmpipe LIBGL_ALWAYS_SOFTWARE=1 xvfb-run -a -s "-screen 0 1920x1080x24 -ac" cadabra2-gtk -n &
APP_PID=$!
sleep 10
if kill -0 $APP_PID 2>/dev/null; then
echo "Application started successfully"
DISPLAY=:99 import -window root screenshot.png
kill $APP_PID
exit 0
else
echo "Application failed to start"
exit 1
fi
- name: Upload screenshot
uses: actions/upload-artifact@v4
with:
name: app-screenshot-ubuntu-24.04
path: screenshot.png
================================================
FILE: .github/workflows/windows-installer.yml
================================================
# Create Windows installer on a github release event. This assumes
# that the cadabra version is the same as the release name, and it
# will attempt to add the installer file to the release assets.
name: Windows 11 installer
# on: [push]
on:
release:
types: [created]
defaults:
run:
shell: msys2 {0}
jobs:
msys2-ucrt64:
strategy:
matrix:
include:
- os: windows-2022
name: x86_64
display-name: "Windows 11 x86_64"
msystem: ucrt64
midfix: ucrt-x86_64
- os: windows-11-arm
name: arm64
msystem: clangarm64
midfix: clang-aarch64
display-name: "Windows 11 arm64"
runs-on: ${{ matrix.os }}
name: Windows 11
defaults:
run:
shell: msys2 {0}
steps:
- name: Install WiX v4/v5 Tool
run: |
dotnet tool install --tool-path c:\WiX\bin wix --version 5.0.2
c:\WiX\bin\wix extension add --global WixToolset.UI.wixext/5.0.2
shell: pwsh
- name: Add WiX toolkit to PATH
shell: bash
run: |
echo "WiX installed at ${WIX}, hopefully..."
echo "Listing C:/WiX/bin"
ls "C:/WiX/bin"
echo "Listing Program Files (x86):"
ls "C:/Program Files (x86)/"
echo "Listing Program Files:"
ls "C:/Program Files/"
echo "${WIX}bin" >> $GITHUB_PATH
- uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.msystem }}
update: true
install: >-
curl
git
mingw-w64-${{ matrix.midfix }}-gcc
mingw-w64-${{ matrix.midfix }}-python
mingw-w64-${{ matrix.midfix }}-gtkmm3
mingw-w64-${{ matrix.midfix }}-boost
mingw-w64-${{ matrix.midfix }}-sqlite3
mingw-w64-${{ matrix.midfix }}-cmake
mingw-w64-${{ matrix.midfix }}-python-matplotlib
mingw-w64-${{ matrix.midfix }}-python-sympy
mingw-w64-${{ matrix.midfix }}-github-cli
- name: Authenticate GitHub CLI
run: gh auth setup-git
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@master
name: Checkout source
- name: Build
run: |
mkdir build
cd build
# Turn off searching Python in the registry, as that contains the
# windows Python which is not the MSYS python that we want to use.
cmake -DPython_FIND_REGISTRY=NEVER ..
ninja
ninja install
cpack
- name: Upload WiX log file
if: always()
uses: actions/upload-artifact@v4
with:
name: wix-${{ matrix.name }}.log
path: build/_CPack_Packages/win64/WIX/wix.log
retention-days: 2
- name: Set version variables from output of cmake
run: |
VER=$(cat build/VERSION)
echo "VERSION=$VER" >> $GITHUB_ENV
GITVER=$(cat build/GIT_TAG_VERSION)
echo "GIT_TAG_VERSION=$GITVER" >> $GITHUB_ENV
- name: Upload release assets
run: |
gh release upload "${{ env.GIT_TAG_VERSION }}" build/cadabra2-${{ env.VERSION }}-win64.msi --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/windows.yml
================================================
# Create Windows installer on a github push or release event
# (upload to the release assets in the latter case).
name: Windows 11
on: [push]
#on:
# release:
# types: [created]
defaults:
run:
shell: msys2 {0}
jobs:
msys2-ucrt64:
strategy:
matrix:
include:
- os: windows-2022
name: x86_64
display-name: "Windows 11 x86_64"
msystem: ucrt64
midfix: ucrt-x86_64
- os: windows-11-arm
name: arm64
msystem: clangarm64
midfix: clang-aarch64
display-name: "Windows 11 arm64"
runs-on: ${{ matrix.os }}
name: Windows build
defaults:
run:
shell: msys2 {0}
steps:
- name: Install WiX v4/v5 Tool
run: |
dotnet tool install --tool-path c:\WiX\bin wix --version 5.0.2
c:\WiX\bin\wix extension add --global WixToolset.UI.wixext/5.0.2
shell: pwsh
- name: Add WiX toolkit to PATH
shell: bash
run: |
echo "WiX installed at ${WIX}, hopefully..."
echo "Listing C:/WiX/bin"
ls "C:/WiX/bin"
echo "Listing Program Files (x86):"
ls "C:/Program Files (x86)/"
echo "Listing Program Files:"
ls "C:/Program Files/"
echo "${WIX}bin" >> $GITHUB_PATH
- uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.msystem }}
update: true
install: >-
curl
git
mingw-w64-${{ matrix.midfix }}-gcc
mingw-w64-${{ matrix.midfix }}-python
mingw-w64-${{ matrix.midfix }}-gtkmm3
mingw-w64-${{ matrix.midfix }}-boost
mingw-w64-${{ matrix.midfix }}-sqlite3
mingw-w64-${{ matrix.midfix }}-cmake
mingw-w64-${{ matrix.midfix }}-python-matplotlib
mingw-w64-${{ matrix.midfix }}-python-sympy
mingw-w64-${{ matrix.midfix }}-github-cli
- name: Authenticate GitHub CLI
run: gh auth setup-git
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@master
name: Checkout source
- name: Build
run: |
mkdir build
cd build
# Turn off searching Python in the registry, as that contains the
# windows Python which is not the MSYS python that we want to use.
cmake -DPython_FIND_REGISTRY=NEVER ..
ninja
ninja install
cpack
- name: Upload cmake_install.cmake file
if: always()
uses: actions/upload-artifact@v4
with:
name: cmake_install-${{ matrix.name }}.cmake
path: build/cmake_install.cmake
retention-days: 2
- name: Upload frontend/cmake_install.cmake file
if: always()
uses: actions/upload-artifact@v4
with:
name: frontend-cmake_install-${{ matrix.name }}.cmake
path: build/frontend/cmake_install.cmake
retention-days: 2
- name: Upload frontend/gtkmm/cmake_install.cmake file
if: always()
uses: actions/upload-artifact@v4
with:
name: frontend-gtkmm-cmake_install-${{ matrix.name }}.cmake
path: build/frontend/gtkmm/cmake_install.cmake
retention-days: 2
- name: Upload WiX log file
if: always()
uses: actions/upload-artifact@v4
with:
name: wix-${{ matrix.name }}.log
path: build/_CPack_Packages/win64/WIX/wix.log
retention-days: 2
- name: Set version variables from output of cmake
run: |
VER=$(cat build/VERSION)
echo "VERSION=$VER" >> $GITHUB_ENV
GITVER=$(cat build/GIT_TAG_VERSION)
echo "GIT_TAG_VERSION=$GITVER" >> $GITHUB_ENV
- name: Upload installer as build artifact
uses: actions/upload-artifact@v4
with:
name: cadabra2-windows-${{ matrix.name }}.msi
path: build/cadabra2-${{ env.VERSION }}-win64.msi
retention-days: 2
- name: Upload release assets
if: github.event_name == 'release'
run: |
mv build/cadabra2-${{ env.VERSION }}-win64.msi build/cadabra2-${{ env.GIT_TAG_VERSION }}-windows-${{ matrix.name }}.msi
gh release upload "${{ env.GIT_TAG_VERSION }}" build/cadabra2-${{ env.GIT_TAG_VERSION }}-windows-${{ matrix.name }}.msi --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
test:
needs: msys2-ucrt64
strategy:
fail-fast: false
matrix:
include:
- os: windows-2022
name: x86_64
display-name: "Windows 11 x86_64"
- os: windows-11-arm
name: arm64
display-name: "Windows 11 arm64"
runs-on: ${{ matrix.os }}
steps:
- name: Download installer
uses: actions/download-artifact@v4
with:
name: cadabra2-windows-${{ matrix.name }}.msi
- name: Install silently
shell: pwsh
run: |
dir
$installer = Get-ChildItem -Filter "cadabra*.msi" | Select-Object -First 1 -ExpandProperty Name
if (-not $installer) {
Write-Error "No installer found"
exit 1
}
$installerPath = (Get-Item -Path ".\$installer").FullName
Write-Host "Installing $installerPath"
Start-Process -FilePath "msiexec" -ArgumentList "/i", "$installerPath", "/qn", "/l*v", "install.log" -Wait
Write-Host "Installer finished"
- name: Upload installation log
uses: actions/upload-artifact@v4
with:
name: install-${{ matrix.os }}.log
path: install.log
- name: Check installation location
shell: cmd
run: |
echo "Main folder:"
dir "C:\Program Files\Cadabra"
- name: Start GUI and take screenshot
shell: pwsh
run: |
# Attempt to start the notebook
if (-not (Test-Path "C:\Program Files\Cadabra\cadabra2-gtk.exe")) {
Write-Host "Executable not found"
exit 1
}
$process = Start-Process "C:\Program Files\Cadabra\cadabra2-gtk.exe" -ArgumentList "-n", "Examples\schwarzschild.cnb" -WorkingDirectory "C:\Program Files\Cadabra" -PassThru
Start-Sleep -Seconds 120
# Take screenshot
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
$bitmap = New-Object System.Drawing.Bitmap($screen.Width, $screen.Height)
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
$graphics.CopyFromScreen($screen.Location, [System.Drawing.Point]::Empty, $screen.Size)
$bitmap.Save("screenshot.png")
$graphics.Dispose()
$bitmap.Dispose()
# Cleanup
Stop-Process -Id $process.Id -Force
- name: Upload cadabra_log.txt
if: always()
uses: actions/upload-artifact@v4
with:
name: cadabra_log-${{ matrix.os }}.txt
path: C:\Windows\Temp\cadabra_log.txt
- name: Upload screenshot
uses: actions/upload-artifact@v4
with:
name: app-screenshot-${{ matrix.os }}
path: screenshot.png
================================================
FILE: .gitignore
================================================
.cache
web2/cadabra2/source/book
*.orig
*.bak
doxygen
.DS_Store
*~
*.o
build
build-lib
jbuild
install
.ipynb_checkpoints
__pycache__
.vscode
.vs
CMakeSettings.json
config/pre_install.rtf
.ccls-cache
*appdata.xml
config/install_script.iss
config/science.cadabra.cadabra2-gtk.desktop
================================================
FILE: .gitmodules
================================================
[submodule "submodules/microtex"]
path = submodules/microtex
url = https://github.com/kpeeters/MicroTeX
branch = kpeeters/cadabra
================================================
FILE: .travis.yml
================================================
branches:
only:
- master
- feature/pybind
os:
- linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-6
env:
- MATRIX_EVAL="CC=gcc-6 && CXX=g++-6"
- osx
compiler:
- gcc
language: cpp
sudo: required
dist: trusty
env:
global:
- CTEST_EXT_COLOR_OUTPUT=TRUE
- CTEST_BUILD_FLAGS=-j4
matrix:
exclude:
- os: osx
compiler: gcc
script:
- cmake --build .
- ctest -VV .
before_script:
- mkdir build
- cd build
- cmake ..
before_install:
- if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then openssl aes-256-cbc -K $encrypted_0c0fd3a7dbd0_key -iv $encrypted_0c0fd3a7dbd0_iv -in config/travisci_rsa.enc -out config/travisci_rsa -d; fi
- if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then chmod 0600 config/travisci_rsa; cp config/travisci_rsa ~/.ssh/id_rsa; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq update; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y cmake python3-dev
g++ libpcre3 libpcre3-dev libgmp3-dev uuid-dev python3-pip; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y libgtkmm-3.0-dev
libjsoncpp-dev libboost-regex-dev libboost-system-dev libboost-program-options-dev libboost-date-time-dev libboost-filesystem-dev libgmp-dev libsqlite3-dev; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; brew unlink json-c; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew outdated cmake || brew upgrade cmake; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew outdated boost || travis_wait brew upgrade boost; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew outdated gmp || brew upgrade gmp; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew outdated pkgconfig || brew upgrade pkgconfig; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install jsoncpp python3 gtkmm3 gnome-icon-theme; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sudo pip3 install matplotlib sympy; fi
- if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then if [[ "`${CC} --version |grep gcc`" != "" ]]; then sudo apt-get install --yes doxygen graphviz texlive-font-utils; ./config/publish-doxygen; fi; fi; fi
install:
- pip3 install --user sympy
notifications:
email:
recipients:
- kasper.peeters@phi-sci.com
on_success: always
on_failure: always
================================================
FILE: CITATION.cff
================================================
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: "Peeters"
given-names: "Kasper"
orcid: "https://orcid.org/0000-0002-3077-8193"
title: "My Research Software"
version: 2.0.4
doi: 10.5281/zenodo.2500762
date-released: 2017-12-18
url: "https://github.com/kpeeters/cadabra2"
preferred-citation:
type: article
authors:
- family-names: "Peeters"
given-names: "Kasper"
orcid: "https://orcid.org/0000-0002-3077-8193"
doi: doi.org/10.21105/joss.01118
journal: "The Journal of Open Source Software"
start: 1118 # First page number
end: 1119 # Last page number
title: "Cadabra2: computer algebra for field theory revisited"
issue: 3
volume: 32
year: 2018
================================================
FILE: CMakeLists.txt
================================================
set(CMAKE_LEGACY_CYGWIN_WIN32 0)
if(WIN32)
# We want to use WiX 4.x but cpack only supports that from 3.30.
set(CADABRA_CMAKE_VERSION 3.30)
else()
set(CADABRA_CMAKE_VERSION 3.12)
endif()
# Policy settings for CMake to resolve ambiguities.
if(POLICY CMP0042)
cmake_policy(SET CMP0042 NEW)
message(STATUS "Set CMake policy CMP0042 to NEW")
endif()
if(POLICY CMP0054)
cmake_policy(SET CMP0054 NEW)
message(STATUS "Set CMake policy CMP0054 to NEW")
endif()
if(POLICY CMP0127)
cmake_policy(SET CMP0127 NEW)
message(STATUS "Set CMake policy CMP0127 to NEW")
endif()
if(POLICY CMP0148)
cmake_policy(SET CMP0148 NEW)
message(STATUS "Set CMake policy CMP0148 to NEW")
endif()
if(POLICY CMP0167)
cmake_policy(SET CMP0167 NEW)
message(STATUS "Set CMake policy CMP0167 to NEW")
endif()
if(POLICY CMP0094)
cmake_policy(SET CMP0094 NEW)
message(STATUS "Set CMake policy CMP0094 to NEW (use first Python found)")
endif()
if(POLICY CMP0169)
cmake_policy(SET CMP0169 NEW)
message(STATUS "Set CMake policy CMP0169 to NEW (use new FetchContent)")
# CMake 3.30: call FetchContent_Populate() with just the name of a
# dependency. This modern alternative was introduced in cmake 3.14
# but we still support 3.12; we call the old behaviour in
# frontend/gtkmm/CMakeLists.txt
endif()
if(POLICY CMP0177)
cmake_policy(SET CMP0177 NEW)
message(STATUS "Set CMake policy CMP0177 to NEW (destination paths are normalised)")
endif()
if(POLICY CMP0087)
cmake_policy(SET CMP0087 NEW)
message(STATUS "Set CMake policy CMP0089 to NEW (evaluate generator expressions in install(CODE))")
endif()
cmake_minimum_required(VERSION ${CADABRA_CMAKE_VERSION})
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project(Cadabra)
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(MACOS TRUE)
endif()
#---------------------------------------------------------------------------
# Preamble
#---------------------------------------------------------------------------
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Aliases for directories
set(CADABRA_ROOT_DIR ${CMAKE_SOURCE_DIR})
set(CADABRA_CLIENT_SERVER_DIR ${CADABRA_ROOT_DIR}/client_server)
set(CADABRA_CORE_DIR ${CADABRA_ROOT_DIR}/core)
set(CADABRA_FRONTEND_DIR ${CADABRA_ROOT_DIR}/frontend)
set(CADABRA_IMAGES_DIR ${CADABRA_ROOT_DIR}/images)
set(CADABRA_LIBS_DIR ${CADABRA_ROOT_DIR}/libs)
include(cmake/functions.cmake)
# Include Visual Studio specific build commands
if (MSVC)
# https://developercommunity.visualstudio.com/content/problem/618088/cmake-msvc-toolset-version-is-incorrect-in-visual.html
if ((MSVC_VERSION EQUAL 1921 OR MSVC_VERSION EQUAL 1922) AND MSVC_TOOLSET_VERSION EQUAL 141)
set(MSVC_TOOLSET_VERSION 142)
endif()
message(STATUS "MSVC_VERSION = ${MSVC_VERSION}, MSVC_TOOLSET_VERSION = ${MSVC_TOOLSET_VERSION}")
include(cmake/windows.cmake)
endif()
# Make sure the build type is non-empty.
if(NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE "Release")
endif()
set(CADABRA_BUILD_TYPE "${CMAKE_BUILD_TYPE}")
if (CMAKE_BUILD_TYPE MATCHES "^Debug$")
set(CADABRA_DEBUG_BUILD TRUE)
endif()
message(STATUS "Build type = ${CMAKE_BUILD_TYPE}")
# Set path to additional cmake files
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules")
if (APPLE)
set(ENV{PKG_CONFIG_PATH} "/usr/local/opt/libffi/lib/pkgconfig:")
endif()
set(PKG_CONFIG_USE_STATIC_LIBS OFF)
if(NOT APPLE)
find_package(TBB QUIET)
if(TBB_FOUND)
message(STATUS "TBB found, will make some operations parallel")
else()
message(STATUS "TBB not found, parallel operations disabled")
endif()
else()
message(STATUS "Apple's toolchain not yet supporting TBB properly, disabled")
endif()
# Get version information.
include(cmake/version.cmake)
print_header("Building Cadabra version ${CADABRA_VERSION_SEM} (${SYSTEM_BITS}-bit)")
message(STATUS "Build id '${CADABRA_VERSION_BUILD}' dated ${CADABRA_VERSION_DATE}")
message(STATUS "Build mode is set to '${CMAKE_BUILD_TYPE}'")
string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} STANDARD_ARCH_NAME)
if(STANDARD_ARCH_NAME STREQUAL "aarch64")
set(STANDARD_ARCH_NAME "arm64")
endif()
if(STANDARD_ARCH_NAME STREQUAL "amd64")
set(STANDARD_ARCH_NAME "x86_64")
endif()
message(STATUS "Architecture is '${CMAKE_SYSTEM_PROCESSOR}' (package names will use '${STANDARD_ARCH_NAME}')")
if(WIN32)
if("${STANDARD_ARCH_NAME}" STREQUAL "x86_64")
set(MSYS_ENV "ucrt64")
set(WIX_SHORT_ARCH "x64")
else()
set(MSYS_ENV "clangarm64")
set(WIX_SHORT_ARCH "arm64")
endif()
message(STATUS "MSYS environment set to ${MSYS_ENV}")
endif()
# Store the version number in a build/VERSION file (so that e.g. github
# actions can pick it up).
file(WRITE build/VERSION "${CADABRA_VERSION_SEM}")
file(WRITE build/GIT_TAG_VERSION "${CADABRA_VERSION_GITHUB_TAG}")
# Notify about install directory
if ("${CMAKE_INSTALL_PREFIX}" STREQUAL "")
message(STATUS "Install directory not set")
else()
message(STATUS "Install directory set to ${CMAKE_INSTALL_PREFIX}")
endif()
# Turn Mathematica support on/off.
option(ENABLE_MATHEMATICA "Enable Mathematica support" OFF)
# Are we trying to build cadabra as a c++ library?
option(BUILD_AS_CPP_LIBRARY "Build cadabra as a C++ library" OFF)
if (BUILD_AS_CPP_LIBRARY)
enable_testing()
add_subdirectory(c++lib)
configure_file(
"${PROJECT_SOURCE_DIR}/core/Config.hh.in"
"${PROJECT_SOURCE_DIR}/core/Config.hh"
)
# Bail out early.
return()
endif()
# Switch between GTK4 and GTK3.
option(USE_GTK4 "Build for GTK4 (instead of GTK3)" OFF)
# Include packaging logic.
include(cmake/packaging.cmake)
#---------------------------------------------------------------------------
# User options and other notifications
#---------------------------------------------------------------------------
#
option(MSVC_TARGET_CONSOLE "Force Release book on MSVC to display a console window" OFF)
option(APPIMAGE_MODE "Run in AppImage mode, overriding path settings" OFF)
if(APPIMAGE_MODE)
message(STATUS "Building for AppImage packaging (Debian paths, MicroTeX)")
if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr")
else()
MESSAGE(FATAL_ERROR "Building with -DAPPIMAGE_MODE=ON also requires -DCMAKE_INSTALL_PREFIX=/usr")
endif()
endif()
option(PACKAGING_MODE "Run in packaging mode, overriding path settings" OFF)
if (PACKAGING_MODE)
message(STATUS "Building in packaging mode")
if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr")
else()
MESSAGE(FATAL_ERROR "Building with -DPACKAGING_MODE=ON also requires -DCMAKE_INSTALL_PREFIX=/usr")
endif()
else()
message(STATUS "Building in user mode")
endif()
option(ENABLE_JUPYTER "Enable building the Xeus-based Jupyter kernel" OFF)
option(ENABLE_PY_JUPYTER "Enable building the default Jupyter kernel" ON)
if(ENABLE_JUPYTER)
# Currently only possible when building against Conda.
set(CONDA_FOUND TRUE)
else()
set(CONDA_FOUND FALSE)
endif()
option(BUILD_TESTS "Build tests" ON)
if (BUILD_TESTS)
message(STATUS "Building tests")
# Allows tests to be built in all subdirectories.
enable_testing()
endif()
option(ENABLE_FRONTEND "Enable the UI frontend" ON)
option(ENABLE_SYSTEM_JSONCPP "Use the system-provided jsoncpp library" OFF)
option(INSTALL_TARGETS_ONLY "Only install targets; skipping icons, shared libraries etc..." OFF)
if (INSTALL_TARGETS_ONLY)
message(STATUS "INSTALL_TARGETS_ONLY is enabled, please make sure all auxillary files and programs Cadabra requires are already installed")
endif()
#---------------------------------------------------------------------------
# Compiler flags.
#---------------------------------------------------------------------------
# - Set the C++ standard to C++17
# - Turn optimizations on
# - Turn off warnings we don't need
include(CheckIPOSupported)
check_ipo_supported(RESULT IPO_SUPPORTED OUTPUT error)
if( IPO_SUPPORTED )
message(STATUS "IPO / LTO enabled")
else()
message(STATUS "IPO / LTO not supported: <${error}>")
endif()
# GCC
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
if (ENABLE_FRONTEND)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
message(FATAL_ERROR "GCC version must be at least 4.9 for regex support! See http://askubuntu.com/questions/428198/getting-installing-gcc-g-4-9-on-ubuntu and then set the environment variables CXX to g++-4.9 and CC to gcc-4.9. You may have to erase the build directory before re-running cmake.")
endif()
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14.0)
message(STATUS "This version of g++ (${CMAKE_CXX_COMPILER_VERSION}) incorrectly warns about possibly uninitialised memory when using std::variant containing a std::shared_ptr. Disabling this warning.")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-maybe-uninitialized")
endif()
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2 -Wall -Wextra -Wunused -Wno-psabi -Wno-unknown-pragmas -Wno-misleading-indentation -fvisibility=hidden -Wno-unused-but-set-variable -Wno-unused-parameter")
endif()
# Clang
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# For Clang, need to additionally check version to avoid compiler bugs
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5)
message(FATAL_ERROR "Clang version must be at least 3.5 to avoid known bugs.")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2 -fvisibility=hidden -Wall -Wextra -Wunused -Wno-unused-parameter -Wno-null-pointer-subtraction")
endif()
# Visual Studio
if(MSVC)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
# Disable specific warnings
set(MSVC_FLAGS
"/wd4250" # inherits via dominance (rampant in the GTKMM codebase)
"/wd4101" # unreferenced local variable
"/wd4244" # conversion from x to y, possible loss of data
"/wd4267" # same as 4244
"/wd4305" # truncation from '' to 'char'
"/wd4309" # truncation of constant value
"/wd4390" # empty control statement, due to a DEBUG macro which requires trailing ;
"/wd4996" # deprecated POSIX functions
"-D_CRT_SECURE_NO_WARNINGS" # don't warn about deprecated functions
"-D_SCL_SECURE_NO_WARNINGS" # don't warn about unsafe function calls (e.g. std::copy with raw pointers)
"-DNOMINMAX" # prevent windows headers from defining min and max macros
"-DWIN32_LEAN_AND_MEAN" # stop windows from including a bunch of garbage
"-DBOOST_ALL_DYN_LINK" # ensure boost's auto-linking is enabled
)
foreach(FLAG ${MSVC_FLAGS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}")
endforeach()
endif()
#---------------------------------------------------------------------------
# Configure the various parts of Cadabra.
#---------------------------------------------------------------------------
# if(MATHEMATICA_FOUND)
# # To avoid issues finding Mathematica's libWSTP64i4,
# # when linking to Mathematica we set the RPATH.
# # That's not something we want to do in general, as e.g. Debian's
# # packages are not supposed to set RPATH.
# SET(CMAKE_SKIP_BUILD_RPATH FALSE)
# SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
# SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
# SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# endif()
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/config/postinst.in"
"${CMAKE_CURRENT_BINARY_DIR}/postinst"
@ONLY
)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/config/install_script.iss.in"
"${CMAKE_CURRENT_SOURCE_DIR}/config/install_script.iss"
)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/config/pre_install.rtf.in"
"${CMAKE_CURRENT_SOURCE_DIR}/config/pre_install.rtf"
)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/config/science.cadabra.cadabra2-gtk.desktop.in"
"${CMAKE_CURRENT_SOURCE_DIR}/config/science.cadabra.cadabra2-gtk.desktop"
)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/frontend/gtkmm/science.cadabra.cadabra2-gtk.appdata.xml.in"
"${CMAKE_CURRENT_SOURCE_DIR}/frontend/gtkmm/science.cadabra.cadabra2-gtk.appdata.xml"
)
#---------------------------------------------------------------------------
# Configure Mathematica (if enabled).
#---------------------------------------------------------------------------
if(ENABLE_MATHEMATICA)
print_header("Configuring Mathematica")
cmake_policy(SET CMP0077 NEW)
set(Mathematica_USE_STATIC_LIBRARIES TRUE)
find_package(Mathematica COMPONENTS WSTP)
endif()
#---------------------------------------------------------------------------
# Configure Python.
#---------------------------------------------------------------------------
print_header("Configuring Python")
include(GNUInstallDirs)
set(Python_POSTFIX "3")
find_package(Python REQUIRED COMPONENTS Interpreter Development)
set(PYTHON_EXECUTABLE ${Python_EXECUTABLE} CACHE INTERNAL "")
set(PYBIND11_PYTHON_VERSION ${Python_VERSION} CACHE INTERNAL "")
find_package(pybind11 CONFIG)
if (NOT pybind11_FOUND OR pybind11_VERSION VERSION_LESS 2.13.6)
if(pybind11_FOUND)
message(STATUS "Found pybind11 with version ${pybind11_VERSION} < 2.13.6, using included pybind11 instead.")
else()
message(STATUS "System-supplied pybind11 not found, using included pybind11.")
endif()
add_subdirectory(libs/pybind11)
endif()
message(STATUS "Found python ${Python_LIBRARIES}")
message(STATUS "Python version is ${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.")
# The PYTHON_SITE_PATH variable is used for installation purposes
# only. It is ideally a path relative to CMAKE_INSTALL_PREFIX, not an
# absolute path which uses this variable explictly. See the CMake docs
# for `install`.
if(WIN32)
set(PYTHON_SITE_PATH lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages)
# NOTE: if you change the CDB_BIN_PATH, you also need to change the stripping
# logic in InstallPrefix.cc. Note that CDB_BIN_PATH needs to be a relative path.
set(CDB_BIN_PATH .)
else()
set(CDB_BIN_PATH bin)
if(PACKAGING_MODE AND IS_DEBIAN_PACKAGE)
# Debian packages install all their Python things in 'dist-packages', not 'site-packages'.
set(PYTHON_SITE_PATH lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/dist-packages)
# set(Python_SITE_DIST "dist-packages")
elseif(APPIMAGE_MODE)
set(PYTHON_SITE_PATH lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/dist-packages)
set(Python_SITE_DIST "dist-packages")
else()
# The builder can override the path by setting it externally.
if(NOT DEFINED PYTHON_SITE_PATH)
# set(PYTHON_SITE_PATH ${Python_SITEARCH})
# For everyone else there is 'site-packages' which we get from
# calling into python's 'site' package (and hoping that the 0th
# element is where we should be writing).
execute_process(
COMMAND ${Python_EXECUTABLE} -c "import site; print(site.getsitepackages()[0])"
OUTPUT_VARIABLE PYTHON_SITE_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()
endif()
endif()
if(IS_ABSOLUTE ${PYTHON_SITE_PATH})
# CPack on windows complains if `install` commands contain absolute paths,
# so we do our best to make PYTHON_SITE_PATH relative to CMAKE_INSTALL_PREFIX.
# Of course, the net effect will be the same, as `install` with a relative
# DESTINATION will prepend CMAKE_INSTALL_PREFIX.
message(STATUS "Making PYTHON_SITE_PATH relative if possible")
string(REPLACE "${CMAKE_INSTALL_PREFIX}/" "" PYTHON_SITE_PATH_REL "${PYTHON_SITE_PATH}")
set(PYTHON_SITE_PATH ${PYTHON_SITE_PATH_REL})
else()
message(STATUS "PYTHON_SITE_PATH is already relative")
endif()
message(STATUS "PYTHON_SITE_PATH = ${PYTHON_SITE_PATH}")
if(IS_ABSOLUTE ${PYTHON_SITE_PATH})
message(STATUS "Installing Cadabra Python module in ${PYTHON_SITE_PATH}")
message(STATUS "Installing Cadabra packages in ${PYTHON_SITE_PATH}/cdb/")
else()
message(STATUS "Installing Cadabra Python module in ${CMAKE_INSTALL_PREFIX}/${PYTHON_SITE_PATH}")
message(STATUS "Installing Cadabra packages in ${CMAKE_INSTALL_PREFIX}/${PYTHON_SITE_PATH}/cdb/")
endif()
message(STATUS "Installing binaries in ${CMAKE_INSTALL_PREFIX}/bin/")
message(STATUS "Installing manual pages in ${CMAKE_INSTALL_PREFIX}/share/man/")
message(STATUS "Installing fonts/icons in ${CMAKE_INSTALL_PREFIX}/share/cadabra2/")
if("${Python_CDB_EXECUTABLE}" STREQUAL "")
# We start the cadabra2 python script by using the current environment,
# so that e.g. Fedora 42 does not hard-code the python path as a
# dependency. However, on macOS with Homebrew, we need to be able
# to override this because otherwise we will not be running in the
# venv which homebrew created for our package. See cadabra2.rb and
# cadabra2-devel.rb where this is used.
set(Python_CDB_EXECUTABLE "/usr/bin/env python3")
endif()
message(STATUS "Starting cadabra2 using '${Python_CDB_EXECUTABLE}'")
message(STATUS "For reference:")
message(STATUS " Python executable (Python_EXECUTABLE) ${Python_EXECUTABLE}")
message(STATUS " Python standard platform-independent installation directory (Python_STDLIB) ${Python_STDLIB}")
message(STATUS " Python standard platform-dependent installation directory (Python_STDARCH) ${Python_STDARCH}")
message(STATUS " Python 3rd-party platform-independent installation directory (Python_SITELIB) ${Python_SITELIB}")
message(STATUS " Python 3rd-party platform-dependent installation directory (Python_SITEARCH) ${Python_SITEARCH}")
# We need to give our Python module an abi-name extension
# so that it can be installed in a folder which does not
# contain the abi name. See
# https://www.python.org/dev/peps/pep-3149/
execute_process(
COMMAND ${Python_EXECUTABLE} -c "import sysconfig; print(sysconfig.get_config_var('SOABI'))"
OUTPUT_VARIABLE Python_SOABI
OUTPUT_STRIP_TRAILING_WHITESPACE
)
message(STATUS "Python abi name ${Python_SOABI}")
# Suffixes
if(WIN32)
set(STATIC_LIB_SUFFIX "lib")
set(SHARED_LIB_SUFFIX "dll")
set(Python_MOD_SUFFIX "pyd")
set(CMAKE_FIND_LIBRARY_PREFIXES "lib" ${CMAKE_FIND_LIBRARY_PREFIXES})
set(CMAKE_SHARED_LIBRARY_SUFFIX ".dll")
set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll.a")
else()
set(STATIC_LIB_SUFFIX "a")
set(SHARED_LIB_SUFFIX "so")
set(Python_MOD_EXT "so")
set(Python_MOD_SUFFIX "${Python_SOABI}.so")
endif()
message(STATUS "Python module extension ${Python_MOD_SUFFIX}")
#---------------------------------------------------------------------------
# Add subdirectories to project.
#---------------------------------------------------------------------------
print_header("Build tools and options")
# Mimalloc makes the cadabra2 module crash when run
# as jupyter kernel, so disable for now.
#find_package(mimalloc 2.0 QUIET)
if(mimalloc_FOUND)
message(STATUS "Using mimalloc allocator")
else()
message(STATUS "Using glibc allocator")
endif()
find_package(Catch2 3 QUIET) # This is optional; we will not run the C++ tests if Catch2 is not found
if(Catch2_FOUND)
message(STATUS "Will run C++ tests with Catch2")
include(Catch)
else()
message(STATUS "Catch2 not found, will not run C++ tests")
endif()
# Jupyter kernel
print_header("Configuring Jupyter kernel")
if(ENABLE_JUPYTER)
message(STATUS "Building the Xeus-based Jupyter kernel")
# Currently only possible when building against Conda.
set(CONDA_FOUND TRUE)
else()
set(CONDA_FOUND FALSE)
if(ENABLE_PY_JUPYTER)
message(STATUS "Building the default Jupyter kernel")
else()
message(STATUS "Not building a Jupyter kernel")
endif()
endif()
if(ENABLE_PY_JUPYTER)
add_subdirectory(jupyterkernel)
endif()
# Core/packages
add_subdirectory(client_server)
add_subdirectory(core)
# Frontend
if(ENABLE_FRONTEND)
set(ENABLE_MICROTEX TRUE)
if(ENABLE_MICROTEX)
set(USE_MICROTEX TRUE)
set(tinyxml2_BUILD_TESTING FALSE)
endif()
add_subdirectory(frontend)
endif()
# Tests
if(BUILD_TESTS)
add_subdirectory(tests)
endif()
add_subdirectory(web2 EXCLUDE_FROM_ALL)
# Generate the core/Config.hh file; this needs to come as late as possible
# in this CMakeLists.txt to ensure that all variables have been set.
configure_file(
"${PROJECT_SOURCE_DIR}/core/Config.hh.in"
"${PROJECT_SOURCE_DIR}/core/Config.hh"
)
# Some additional logic to install all runtime dependencies of our binaries
# into the target installation directory on windows.
if(WIN32)
set(EXECUTABLES
core/cadabra2-cli
core/cdb-nbtool
client_server/cadabra-server
frontend/gtkmm/cadabra2-gtk
)
list(JOIN EXECUTABLES " " LEXECUTABLES)
# Custom command to run ldd, get dependencies, and install these in a
# folder ready to be processed by `install`. The 'ldd' command does not
# run on the '*.pyd' file, but if we rename or copy it to have extension '.dll'
# all goes through fine...
set(LDDSTR "ldd /${MSYS_ENV}/lib/gdk-pixbuf-2.0/2.10.0/loaders/pixbufloader_svg.dll core/cadabra2.dll ${LEXECUTABLES} | sed -e '/not found/d' -e '/Windows/d' -e '/System32/d' -e '/SysWOW64/d' | grep '=>' | sed -e 's/^[^=]*=>[ ]*\\([^ ]*\\).*/\\1/' | sort | uniq > ${CMAKE_BINARY_DIR}/ldd_dependencies.txt")
message(STATUS "Determining dependencies using command ${LDDSTR}")
add_custom_command(
OUTPUT dummy1
COMMAND cp core/cadabra2.pyd core/cadabra2.dll
COMMAND ${CMAKE_COMMAND} -E env bash -c "${LDDSTR}"
COMMAND touch dummy1
VERBATIM
DEPENDS core/cadabra2.pyd ${EXECUTABLES}
COMMENT "Using ldd to determine dependencies of ${EXECUTABLES}"
)
add_custom_command(
OUTPUT dummy2
DEPENDS dummy1
COMMAND ${CMAKE_COMMAND} -E env bash -c "cat ${CMAKE_BINARY_DIR}/ldd_dependencies.txt && mkdir -p ${CMAKE_SOURCE_DIR}/deps && for f in `cat ${CMAKE_BINARY_DIR}/ldd_dependencies.txt`; do cp \${f} ${CMAKE_SOURCE_DIR}/deps/; done"
COMMAND touch dummy2
VERBATIM
COMMENT "Copying dependencies into ${CMAKE_SOURCE_DIR}/deps"
)
add_custom_target("do_deps" ALL DEPENDS dummy2)
# Install all the dependencies into the root destination folder.
install(DIRECTORY deps/ DESTINATION . FILES_MATCHING PATTERN "*.dll")
# And install a few more which, for reasons unknown, are not reported by dll.
install(CODE "execute_process(COMMAND ls \"/${MSYS_ENV}/bin\") " )
winstall(FILES /${MSYS_ENV}/bin/libcharset-1.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/librsvg-2-2.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/libxml2-2.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/liblzma-5.dll DESTINATION .)
# FIXME: these can be found by running ldd on numpy and matplotlib dlls.
winstall(FILES /${MSYS_ENV}/bin/libopenblas.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/libgomp-1.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/libgfortran-5.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/libquadmath-0.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/libsharpyuv-0.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/libjpeg-8.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/libtiff-6.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/libdeflate.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/libjbig-0.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/libLerc.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/libwebp-7.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/libzstd.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/libimagequant.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/libopenjp2-7.dll DESTINATION .)
# These are necessary only on ARM64.
winstall(FILES /${MSYS_ENV}/bin/libgmp-10.dll DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/libgmpxx-4.dll DESTINATION .)
# We need gdbus to setup the dbus, needed by Glib, otherwise anything
# gtk-related will just bail out at start. We also need the helper
# program to spawn programs using Glib.
winstall(FILES /${MSYS_ENV}/bin/gdbus.exe DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/gspawn-win64-helper.exe DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/gspawn-win64-helper-console.exe DESTINATION .)
winstall(FILES /${MSYS_ENV}/bin/gdk-pixbuf-query-loaders.exe DESTINATION .)
endif()
#---------------------------------------------------------------------------
# Provide uninstall target.
#---------------------------------------------------------------------------
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake"
IMMEDIATE @ONLY
)
add_custom_target(uninstall
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake"
)
#---------------------------------------------------------------------------
# Provide target to build AppImage.
#---------------------------------------------------------------------------
# if(APPIMAGE_MODE)
# add_custom_target(appimage
# COMMAND mkdir -p AppDir/${Python_SITELIB}
# COMMAND cp -a ${CMAKE_SOURCE_DIR}/config/AppRun AppDir/
# COMMAND chmod gou+x ${CMAKE_SOURCE_DIR}/config/AppRun AppDir/AppRun
# COMMAND cp -a ${Python_SITELIB}/setuptools AppDir/${Python_SITELIB}/
# COMMAND cp -a ${Python_STDARCH}/* AppDir/${Python_STDARCH}/
# )
# endif()
if(WIN32)
add_custom_target(windows-installer
COMMAND cpack
# COMMAND osslsigncode sign -pkcs12 "/mnt/c/path/to/certificate.p12" -pass "certificate password" -n "Cadabra2" -i "https://cadabra.science" -t "http://timestamp.comodoca.com/authenticode" -in "cadabra2-${CADABRA_VERSION_SEM}-win64.exe" -out "cadabra2-${CADABRA_VERSION_SEM}-win64-installer.exe"
COMMAND gh auth setup-git
COMMAND release upload "${CADABRA_VERSION_SEM}" cadabra2-${CADABRA_VERSION_SEM}-win64.msi --clobber
)
endif()
print_header("All scripts completed")
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at kasper.peeters@phi-sci.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
================================================
FILE: CONTRIBUTING.md
================================================
How to contribute
=================
If you want to help out with Cadabra, or think you can contribute a
useful add-on package or perhaps just a sample notebook or a bit of
documentation, you are more than welcome! Even just dropping us a
note with some details about what computations you do with Cadabra
is a useful contribution, and helps to get a better idea of what
people expect from the software.
Below are some tips on how to get started contributing to Cadabra. For
any questions, please post in the [questions and answers][1] forum or
get in touch directly via [email][2]
Get familiar with the software
------------------------------
The first thing to do is to get familiar with how the software works,
by playing with the [tutorials][3] Details of the software, including
some of the logic that is behind its inner workings, are described in
the [reference guide][4], and documentation is available separately
for all [properties and algorithms][5]. If you get stuck, do not
hesitate to post a question on the [questions and answers][1] site. If
you want to contribute at the level of the C++ core, or if you are
simply interested in how things work behind the scenes, you will find
the [doxygen][6] documentation useful.
Identify something to work on
-----------------------------
Most likely you will have used (or will have tried to use) Cadabra for
a concrete project, and found that you see room for improvement. This
can be as simple as reporting a bug, perhaps even fixing it, or adding
a tutorial or other documentation, providing add-on functionality in
the form of packages, or helping with the core. We are also always
interested to hear about issues installing Cadabra on new systems.
Setup your development environment
----------------------------------
Contributions in any form are welcome, but if you want to submit
substantial code contributions, it is useful to get familiar with the
git version control system. This is used to track software changes
over time and to effectively manage contributions from different
authors. We also utilise [github][7], a web interface to git,
extensively and use it for communication, issue tracking, merging
patches (pull requests) and so on. Check out the source code from
there and follow the instructions on how to build it.
Code conventions
----------------
To generate debug output, we use the [dbg][8] facility. To turn this
on for a particular source file, comment out the `DBG_MACRO_DISABLE`
definition at the top. You will then get nicely formatted debug output
while running.
Code is formatted using tabs for indentation, K&R style braces,
with some fine-tuning. All is taken care of by `make format` in the
top-level directory, which runs through the entire source tree and
does a reformat using `astyle`.
Documentation
-------------
We have tutorials, manual pages and reference documentation. In
addition, there are user-contributed notebooks. In this sense, we try
to follow [divio][9]. Contributions to all of these parts of the
documentation are welcome.
[1] https://cadabra.science/qa/
[2] mailto:info@cadabra.science
[3] https://cadabra.science/tutorials.html
[4] https://cadabra.science/help.html
[5] https://cadabra.science/man.html
[6] https://cadabra.science/doxygen/html/
[7] https://github.com/kpeeters/cadabra2
[8] https://github.com/sharkdp/dbg-macro
[9] https://www.divio.com/blog/documentation/
================================================
FILE: JUPYTER.rst
================================================
Building the Cadabra Jupyter kernel
===================================
The Cadabra build scripts can now build a Jupyter kernel, so that you
can use the Jupyter notebook to write Cadabra code (using all of the
Cadabra notation, i.e. without having to resort to the much more ugly
Python interface). At the moment this is only supported by compiling
against a Conda python, simply because that enables us to build on the
'xeus' library more easily.
Building a Conda package
------------------------
After installation, first activate your miniconda distribution::
source ~/miniconda3/bin/activate
All dependencies to build a Conda package of Cadabra can then be
installed from Conda directly, with::
conda install conda-build
Then build with::
cd conda
conda-build .
To install::
conda install --use-local cadabra2
Building using Conda (old)
--------------------------
The following instructions have been tested on a clean Ubuntu 18.04
installation.
The Cadabra Jupyter kernel uses the Xeus library, which is most easily
obtained by getting it from Conda. If you do not have Conda yet, get
a minimal installation (MiniConda) from
https://docs.conda.io/en/latest/miniconda.html
(install a Python3.x version).
When building against Conda, Cadabra will build only the Python module
and the cadabra-jupyter-kernel binary. It is not possible to build
many of the other parts of Cadabra using Conda, for various reasons:
Conda's glibmm is not built with C++11 enabled, there is no gtkmm
library, and probably others. For a discussion on this, see
https://groups.google.com/a/continuum.io/d/msg/anaconda/oHtExJU9oiM/oMZLGpn1CAAJ
and if you don't think this is a problem, see e.g.
https://unix.stackexchange.com/questions/414904/anaconda-qt-vs-system-qt
After installation, first activate your miniconda distribution::
source ~/miniconda3/bin/activate
All dependencies for Cadabra's Jupyter kernel can then be installed from
Conda directly, with::
conda install cmake pkg-config glibmm zeromq cppzmq xtl cryptopp \
sqlite util-linux xeus nlohmann_json sympy \
jupyter -c conda-forge
Now it is time to do the Cadabra build. Configure with options which
ensure that CMake picks up the Conda libraries first, and make it
install the Cadabra things in a place which does not interfere with
any 'normal' build you may have sitting around::
cd cadabra2
mkdir build
cd build
cmake -DENABLE_JUPYTER=ON -DENABLE_FRONTEND=OFF \
-DCMAKE_INCLUDE_PATH=${HOME}/miniconda3/include \
-DCMAKE_LIBRARY_PATH=${HOME}/miniconda3/lib \
-DCMAKE_INSTALL_PREFIX=${HOME}/miniconda3 \
..
You should see that it has configured using the Conda Python; look for
the `Configuring Python` block, which should be something like::
-------------------------------------------
Configuring Python
-------------------------------------------
-- Building for use with Python 3 (good!)
-- Found PythonInterp: /home/kasper/miniconda3/bin/python3.7 (found version "3.7.1")
-- Found PythonLibs: /home/kasper/miniconda3/lib/libpython3.7m.so
-- pybind11 v2.3.dev0
-- Found python /home/kasper/miniconda3/lib/libpython3.7m.so
Note the reference to the Conda installation path. Further down you
should then also see a block for the Jupyter kernel::
-------------------------------------------
Configuring Jupyter kernel build
-------------------------------------------
If that's all ok, you can build with the standard::
make
sudo make install
This will install the kernel in::
${HOME}/miniconda3/bin/
and the JSON configuration files in::
${HOME}/miniconda3/share/jupyter/kernels/cadabra/
If you now start Jupyter, you should be able to choose a Cadabra
kernel::
${HOME}/miniconda3/bin/jupyter notebook
There is a sample `schwarzschild.ipynb` in the `examples` directory.
Setting up a Jupyterhub server for Cadabra
------------------------------------------
The following instructions setup a JupyterHub installation using 'The
Littlest JupyterHub' (TLJH). These instructions have been tested on a
clean Ubuntu 18.04 installation.
First install TLJH as per the instructions at::
https://the-littlest-jupyterhub.readthedocs.io/en/latest/install/custom-server.html
(note that you *first* need to do a sudo command, otherwise the
installer will ask for the password but you won't see that prompt,
making it look like the installation process hangs).
*New*: it should now also be possible to simply install the conda
package for the Jupyter kernel. *New*
Become root (you cannot write into `/opt/tljh` otherwise) and set the
conda path using::
sudo su
export PATH=/opt/tljh/user/bin:${PATH}
Install the prerequisites with::
conda install cmake pkg-config glibmm zeromq cppzmq xtl cryptopp \
sqlite util-linux xeus nlohmann_json sympy \
-c conda-forge
Build the Cadabra Jupyter kernel with::
cd cadabra2
mkdir build
cd build
cmake -DENABLE_JUPYTER=ON -DENABLE_FRONTEND=OFF \
-DCMAKE_INCLUDE_PATH=/opt/tljh/user/include \
-DCMAKE_LIBRARY_PATH=/opt/tljh/user/lib \
-DCMAKE_INSTALL_PREFIX=/opt/tljh/user/ \
..
make install
The 'new' button in the Jupyterhub file browser should now offer you
the option of creating a new Cadabra notebook.
Creating a Conda package of the Cadabra Jupyter kernel
------------------------------------------------------
To build a Conda package of the Cadabra Jupyter kernel from scratch,
first install miniconda as above, and activate::
source ~/miniconda3/bin/activate
Now the fun starts. Conda is an absolutely horrendous packaging
system, which absolutely does *not* get dependencies right, but we
will have to live with it. First, update the base conda distribution::
conda update -n base -c defaults conda
conda update --all
Then activate the `conda-forge` channel, and update to the latest of
everything::
conda config --add channels conda-forge
conda update --all
Do *not* use `conda config --set channel_priority strict` as that
*will* break the build with an endless list of package conflicts.
There are other ways to add the conda-forge channel, all subtly
different; avoid adding `-c conda-forge` as that is just broken beyond
belief too. Now install the prerequisites for building conda
packages::
conda install conda-build anaconda-client \
xeus pkg-config glibmm
That last line should not have been necessary, as build requirements
in `meta.yaml` should have taken care of it, but alas, it does not
work that way. It spits out various messages about packages being
*downgraded*; don't ask, I told you the system was broken.
Now change to the `conda` directory and build the package::
cd cadabra2/conda
export PKG_CONFIG_PATH=${HOME}/miniconda3/lib/pkgconfig
conda build .
Again, that path setting should have been handled automatically...
To upload::
anaconda login
anaconda upload /path/to/conda-package.tar.bz2
================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{one line to give the program's name and a brief idea of what it does.}
Copyright (C) {year} {name of author}
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
{project} Copyright (C) {year} {fullname}
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
================================================
FILE: Makefile
================================================
all:
@echo -n "\nTo build Cadabra, \n\n mkdir build\n cd build\n cmake ..\n make\n\nThe other targets here are (for maintainer purposes only)\n\n tarball: build a tarball cadabra2-latest.tar.gz of current HEAD\n doc: generate doxygen docs in doc\n webup: build web pages/tutorials/man pages and upload to server\n updatesnoop: sync snoop repo\n packages: create deb/rpm packages on buildbot\n\nIf you need help, email info@cadabra.science\n\n"
.PHONY: doc tarball findclay webbuild claybuild webup format packages updatesnoop
tarball:
git archive --format=tar --prefix=cadabra2-latest/ HEAD | gzip > ${HOME}/tmp/cadabra2-latest.tar.gz
doc:
doxygen config/Doxyfile
webbuild:
cd build; make -f web2/Makefile
claybuild:
cd web2/cadabra2/source; rm -Rf build; clay build
CMD_NOT_FOUND = $(error $(1) is required for this rule)
CHECK_CMD = $(if $(shell command -v $(1)),,$(call CMD_NOT_FOUND,$(1)))
findclay:
$(call CHECK_CMD, clay)
webup: findclay webbuild claybuild
doxygen config/Doxyfile
rsync -avz --chmod=+rx doxygen/ cadabra_web:/var/www/cadabra2/doxygen/
cd web2/cadabra2/source; rsync -avz --chmod=+rx build/ cadabra_web:/var/www/cadabra2/; rsync -avz --chmod=+rx static/styles/ cadabra_web:/var/www/cadabra2/static/styles; scp static/cadabra_in* cadabra_web:/var/www/cadabra2/static/; rsync -avz --chmod=+rx static/fonts/ cadabra_web:/var/www/cadabra2/static/fonts; rsync -avz --chmod=+rx static/images/ cadabra_web:/var/www/cadabra2/static/images/; rsync -avz --chmod=+rx static/icons/ cadabra_web:/var/www/cadabra2/static/icons/; rsync -avz --chmod=+rx static/pdf/ cadabra_web:/var/www/cadabra2/static/pdf/; rsync -avz --chmod=+rx static/js/ cadabra_web:/var/www/cadabra2/static/js/; rsync -avz --chmod=+r static/robots.txt cadabra_web:/var/www/cadabra2
format:
astyle --style=k/r --indent=tab=3 --recursive --attach-classes --attach-namespaces --indent-classes --indent-namespaces --indent-switches --break-closing-braces '*.hh'
astyle --style=k/r --indent=tab=3 --recursive --attach-classes --attach-namespaces --indent-classes --indent-namespaces --indent-switches --break-closing-braces '*.cc'
find . -name "*.cc" -exec sed -i -e 's/^\([ \t]*\)\([\{\}]\)/\1\t\2/' '{}' ';'
find . -name "*.hh" -exec sed -i -e 's/^\([ \t]*\)\([\{\}]\)/\1\t\2/' '{}' ';'
packages:
bash config/buildbot.sh
appimage:
(mkdir build-appimage; cd build-appimage; cmake -DAPPIMAGE_MODE=ON -DCMAKE_INSTALL_PREFIX=/usr ..; make; make install DESTDIR=AppDir; make appimage)
updatesnoop:
cp ../snoop/src/Snoop.cc ../snoop/src/SnoopPrivate.hh ../snoop/src/Snoop.hh client_server/
================================================
FILE: README.rst
================================================
Cadabra
=======
.. image:: https://joss.theoj.org/papers/10.21105/joss.01118/status.svg
:target: https://doi.org/10.21105/joss.01118
.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.2500762.svg
:target: https://doi.org/10.5281/zenodo.2500762
.. image:: https://github.com/kpeeters/cadabra2/workflows/Linux/badge.svg
:target: https://github.com/kpeeters/cadabra2/actions?query=workflow%3ALinux
.. image:: https://github.com/kpeeters/cadabra2/workflows/macOS/badge.svg
:target: https://github.com/kpeeters/cadabra2/actions?query=workflow%3AmacOS
.. image:: https://github.com/kpeeters/cadabra2/workflows/Docker/badge.svg
:target: https://github.com/kpeeters/cadabra2/actions?query=workflow%3ADocker
.. image:: https://github.com/kpeeters/cadabra2/workflows/Windows%2011/badge.svg
:target: https://github.com/kpeeters/cadabra2/actions?query=workflow%3AWindows%2011
.. image:: https://github.com/kpeeters/cadabra2/workflows/FreeBSD/badge.svg
:target: https://github.com/kpeeters/cadabra2/actions?query=workflow%3AFreeBSD
*A field-theory motivated approach to computer algebra.*
Kasper Peeters
- End-user documentation at https://cadabra.science/
- Source code documentation at https://cadabra.science/doxygen/html
This repository holds the 2.x series of the Cadabra computer algebra
system. It supersedes the 1.x series, which can still be found at
https://github.com/kpeeters/cadabra.
Cadabra is a symbolic computer algebra system, designed specifically
for the solution of problems encountered in quantum and classical
field theory. It has extensive functionality for tensor computer
algebra, tensor polynomial simplification including multi-term
symmetries, fermions and anti-commuting variables, Clifford algebras
and Fierz transformations, implicit coordinate dependence, multiple
index types and many more. The input format is a subset of TeX. Both a
command-line and a graphical interface are available, and there is a
kernel for Jupyter.
Installation
-------------
Cadabra builds on Linux, macOS, OpenBSD, FreeBSD and Windows. Select
your system from the list below for detailed instructions.
- `Linux (Debian/Ubuntu/Mint)`_
- `Linux (Fedora 24 and later)`_
- `Linux (CentOS/Scientific Linux)`_
- `Linux (openSUSE)`_
- `Linux (Arch/Manjaro)`_
- `Linux (Solus)`_
- `OpenBSD`_
- `FreeBSD`_
- `macOS`_
- `Windows`_
Binaries for most of these platforms are provided from the download
page at https://cadabra.science/download.html, which links to
https://github.com/kpeeters/cadabra2/releases/latest. These binaries
are automatically generated on every release.
See `Building Cadabra as C++ library`_ for instructions on how to
build the entire Cadabra functionality as a library which you can use
in a C++ program.
See `Building a Jupyter kernel`_ for information on the Jupyter kernel
for Cadabra sessions.
See `Notes on Python paths`_ for some remarks on where Cadabra
installs its Python modules and how this plays with various types of
Python installations.
Linux (Debian/Ubuntu/Mint)
~~~~~~~~~~~~~~~~~~~~~~~~~~
On Debian/Ubuntu you can install all that is needed with::
sudo apt install git cmake libpython3-dev python3-dev g++ libgmp3-dev \
libgtkmm-3.0-dev libboost-all-dev libssl-dev libgmp-dev libsqlite3-dev uuid-dev \
python3-matplotlib python3-mpmath python3-sympy python3-gmpy2
(on Ubuntu 14.04 you need to replace `cmake` with `cmake3` and also
install g++-4.9; get in touch if you don't know how to do this). On
older systems you may want to install `sympy` using `sudo pip3 install
sympy`, but that is discouraged in general.
This is the development platform and issues are typically first fixed
here. You can use either g++ or the clang++ compiler to build. You need to
clone the cadabra2 git repository (if you download the .zip file you
will not have all data necessary to build). So first do::
git clone https://github.com/kpeeters/cadabra2
Building is then done with the standard::
cd cadabra2
mkdir build
cd build
cmake ..
make
sudo make install
This will produce the command line app ``cadabra2`` and the Gtk
notebook interface ``cadabra2-gtk``. You can also find the latter in
the 'Education' menu.
Linux (Fedora 24 and later)
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Fedora 24 is the first Fedora to have Python 3; you can build Cadabra
using Python 2 but you are strongly encouraged to upgrade. The Fedora
platform receives less testing so please get in touch if you run into
any issues. You can use either g++ or the clang++ compiler.
Install the dependencies with::
sudo dnf install git python3-devel make cmake gcc-c++ \
gmp-devel libuuid-devel sqlite-devel \
gtkmm30-devel boost-devel \
python3-matplotlib \
python3-pip
sudo pip3 install sympy
You need to clone the cadabra2 git repository (if you download the
.zip file you will not have all data necessary to build). So first do::
git clone https://github.com/kpeeters/cadabra2
Building is then done with the standard::
cd cadabra2
mkdir build
cd build
cmake ..
make
sudo make install
This will produce the command line app ``cadabra2`` and the Gtk
notebook interface ``cadabra2-gtk``. You can also find the latter
when searching for the 'Cadabra' app from the 'Activities' menu.
Linux (CentOS/Scientific Linux)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
On CentOS/Scientific Linux you need to activate The Software
Collections (SCL) and Extra Packages for Enterprise Linux (EPEL) to
get access to a modern C++ compiler, Python3 and all required build
tools.
On *CentOS* first do::
sudo yum install centos-release-scl epel-release
On *Scientific Linux* the equivalent is::
sudo yum install yum-conf-softwarecollections epel-release
Now install all build dependencies with::
sudo yum install devtoolset-7 rh-python36 cmake3 \
gmp-devel libuuid-devel sqlite-devel \
gtkmm30-devel boost-devel git \
python-matplotlib
You need to enable the Python3 and C++ compiler which you just
installed with::
scl enable rh-python36 bash
scl enable devtoolset-7 bash
(note: do *not* use sudo here!).
You also need to install sympy by hand::
sudo pip3 install sympy
Now need to clone the cadabra2 git repository (if you download the
.zip file you will not have all data necessary to build)::
git clone https://github.com/kpeeters/cadabra2
Building is then done with the standard::
cd cadabra2
mkdir build
cd build
cmake3 ..
make
sudo make install
This will produce the command line app ``cadabra2`` and the Gtk
notebook interface ``cadabra2-gtk``. You can also find the latter in
the 'Education' menu.
Linux (openSUSE)
~~~~~~~~~~~~~~~~
For openSUSE (tested on 'Leap 15.2', probably also fine with minor
changes for 'Tumbleweed') you first need to install the dependencies
with::
sudo zypper install --no-recommends git cmake python3-devel gcc-c++ \
gmp-devel libuuid-devel sqlite-devel \
gtkmm3-devel \
python3-matplotlib \
python3-sympy \
libboost_system1_71_0-devel libboost_filesystem1_71_0-devel \
libboost_date_time1_71_0-devel libboost_program_options1_71_0-devel
This platform receives less testing so please get in touch if you run
into any issues. You need to clone the cadabra2 git repository (if you
download the .zip file you will not have all data necessary to
build). So first do::
git clone https://github.com/kpeeters/cadabra2
Building is then done with the standard::
cd cadabra2
mkdir build
cd build
cmake ..
make
sudo make install
This will produce the command line app ``cadabra2`` and the Gtk
notebook interface ``cadabra2-gtk``.
Linux (Arch/Manjaro)
~~~~~~~~~~~~~~~~~~~~
The package for Arch Linux is cadabra2
https://aur.archlinux.org/packages/cadabra2/ Building and
installing (including dependencies) can be accomplished with::
yay -S cadabra2
Alternatively use ``makepkg``::
git clone https://aur.archlinux.org/cadabra2.git
cd cadabra2
makepkg -si
Please consult the Arch Wiki
https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages
for more information regarding installing packages from the AUR.
Linux (Solus)
~~~~~~~~~~~~~
Support for Solux Linux is experimental. To build from source on Solus
Linux, first install the dependencies by doing::
sudo eopkg install -c system.devel
sudo eopkg install libboost-devel gmp-devel libgtkmm-3-devel
sudo eopkg install sqlite3-devel python3-devel
sudo eopkg install git cmake make g++
Then configure and build with::
cd cadabra2
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/usr
make
sudo make install
This installs below ``/usr`` (instead of ``/usr/local`` on other
platforms) because I could not figure out how to make it pick up
libraries there.
Any feedback on these instructions is welcome.
OpenBSD
~~~~~~~
Install the dependencies with::
pkg_add git cmake boost python-3.6.2 gtk3mm gmp gmpxx py3-sympy
We will build using the default clang-4.0.0 compiler; building with
the alternative g++-4.9.4 leads to trouble when linking against the
libraries added with pkg_add.
Configure and build with::
cd cadabra2
mkdir build
cd build
cmake -DENABLE_MATHEMATICA=OFF ..
make
su
make install
The command-line version is now available as ``cadabra2`` and the
notebook interface as ``cadabra2-gtk``.
Any feedback on this platform is welcome as this is not our
development platform and testing is done only occasionally.
FreeBSD
~~~~~~~
The recommended way to install Cadabra is through::
pkg install cadabra2
It is also possible to build and install Cadabra from the port::
cd /usr/ports/math/cadabra2 && make install clean
The command-line version is now available as ``cadabra2`` and the
notebook interface as ``cadabra2-gtk``.
Any feedback on this platform is welcome as this is not our
development platform.
macOS
~~~~~
Cadabra builds with the standard Apple compiler, on both Intel and
Apple silicon, but you do need a number of packages from Homebrew (see
https://brew.sh). Install the required dependencies with::
brew install cmake boost gmp python3
brew install pkgconfig
brew install gtkmm3 adwaita-icon-theme
pip3 install sympy gmpy2
If the lines above prompt you to install XCode, go ahead and let it do
that.
You can build against an Anaconda Python installation (in case you
prefer Anaconda over the Homebrew Python); cmake will automatically
pick this up if available.
You need to clone the cadabra2 git repository (if you download the
.zip file you will not have all data necessary to build). So do::
git clone https://github.com/kpeeters/cadabra2
After that you can build with the standard::
cd cadabra2
mkdir build
cd build
cmake -DENABLE_MATHEMATICA=OFF ..
make
sudo make install
(*note* the `-DENABLE_MATHEMATICA=OFF` in the `cmake` line above; the
Mathematica scalar backend does not yet work on macOS).
This will produce the command line app ``cadabra2`` and the Gtk
notebook interface ``cadabra2-gtk``.
Feedback from macOS users is *very* welcome because this is not the main
development platform.
Windows
~~~~~~~
On Windows compilation is easiest by using the MSYS2 system, as their
gtkmm-3.0 packages just work and the whole system can be driven from the
command line. We used to build Cadabra using the vcpkg packages, but
they no longer provide packages for gtkmm-3.0, and in general the lack
of binary packages means that build times are on the order of many, many
hours, instead of just a few minutes with MSYS2. More info on building and
packaging gtk apps on windows at https://www.gtk.org/docs/installations/windows/.
Install MSYS2 from https://www.msys2.org and start a UCRT64 shell.
First update with (if you don't do this you may end up not being able
to install some of the required packages due to version conflicts)::
pacman -Suy
Then install a compiler and the dependencies of Cadabra with::
pacman -S mingw-w64-ucrt-x86_64-gcc
pacman -S mingw-w64-ucrt-x86_64-gtkmm3
pacman -S mingw-w64-ucrt-x86_64-boost
pacman -S mingw-w64-ucrt-x86_64-sqlite3
pacman -S mingw-w64-ucrt-x86_64-cmake
pacman -S mingw-w64-ucrt-x86_64-python
pacman -S mingw-w64-ucrt-x86_64-python-matplotlib
pacman -S mingw-w64-ucrt-x86_64-python-sympy
pacman -S mingw-w64-ucrt-x86_64-osslsigncode
pacman -S git
Checkout Cadabra and build::
git clone https://github.com/kpeeters/cadabra2
cd cadabra2
mkdir build
cd build
cmake ..
ninja
ninja install
This will leave an installation in `Program Files (x86)/Cadabra`, from where
you can start `cadabra2-gtk`.
To build an installer, simply run `cpack` after having built
Cadabra.
Building a Jupyter kernel
-------------------------
As of version 2.3.4 the standard build process (as described above)
also creates a Jupyter kernel, which is written in Python on top of
`ipykernel` (thanks to Fergus Baker). This should work on most
platforms out-of-the-box; you do not need to do anything else. The
Jupyter kernel allows you to use Cadabra notation inside a Jupyter
notebook session.
The distribution also still contains code for the 'old' Jupyter
kernel, which is written in C++ on top of `xeus`. Building this kernel
is more complicated mainly because of this dependency, and there is
not much of an advantage over the Python kernel; it's mainly left in
the tree for future reference, For full instructions on how to build
the old `xeus`-based kernel, see
https://github.com/kpeeters/cadabra2/blob/master/JUPYTER.rst.
Creating an AppImage
--------------------
The Cadabra build system can create an AppImage for use on a wide
variety of Linux distributions (this is used to create the AppImage
which is available from the `releases` on github). This build process
has been tested by using Ubuntu 20.04 as base system. Install the
prerequisites as above,::
sudo apt install git cmake libpython3-dev python3-dev g++ libgmp3-dev \
libgtkmm-3.0-dev libboost-all-dev libssl-dev libgmp-dev libsqlite3-dev uuid-dev \
python3-matplotlib python3-mpmath python3-sympy python3-gmpy2
Now configure and build with::
cmake -DAPPIMAGE_MODE=ON -DCMAKE_INSTALL_PREFIX=/usr ..
make
make install DESTDIR=AppDir
This installs everything in the `AppDir` folder ready for packaging.
Then run::
make appimage
to create the AppImage itself. If you run into trouble with this,
please first consult the comments in the top-level `CMakeLists.txt`
file about `linuxdeploy` and friends.
Tutorials and other help
------------------------
Please consult https://cadabra.science/ for tutorial-style notebooks
and all other documentation, and https://cadabra.science/doxygen/html/
for doxygen documentation of the current master branch. The latter can
also be generated locally; you will need (on Debian and derivatives)::
sudo apt-get install doxygen libjs-mathjax
For any questions, please contact info@cadabra.science .
Building Cadabra as C++ library
-------------------------------
If you want to use the functionality of Cadabra inside your own C++
programs, you can build Cadabra as a shared library. To do this::
mkdir build-lib
cd build-lib
cmake -DBUILD_AS_CPP_LIBRARY=ON ..
make
sudo make install
There is a sample program `simple.cc
`_
in the `c++lib` directory which shows how to use the Cadabra library.
Notes on Python paths
---------------------
Cadabra tries to play nice with a large variety of Python
installations, which is not an easy task. In general, it will try to
install in such a way that the Python interpreter which is specified
at build time will be able to import the `cadabra2` Python module
without any change to its path. This is necessary so that e.g. a
Jupyter notebook will be able to find this module. Cadabra will
therefore install its Python module in
`site.getsitepackages()[0]`. Since this module constructs its
docstrings dynamically on load, the manual pages are also stored
relative to this module.
However, Cadabra will install its binaries according to standard
CMake logic in `$CMAKE_INSTALL_PREFIX/bin/`. On systems that have
Python installed in subtree which is not below `$CMAKE_INSTALL_PREFIX`,
this means that the Cadabra binaries and the Cadabra Python module
will not be in the same subtree. This typically happens on systems
with Python coming from Homebrew, as these will have Python somewhere
below `/opt/homebrew` even when `$CMAKE_INSTALL_PREFIX` is `/usr/local/`.
On some systems, users or package managers prefer that
`site.getsitepackages()[0]` remains under control of the package
manager (Homebrew is the typical example). In this case, if you do not
want to write there, your only option is to first create a virtual
environment before you run Cadabra's `cmake`.
Special thanks
--------------
Special thanks to José M. Martín-García (for the xPerm
canonicalisation code), James Allen (for writing much of the factoring
code), Dominic Price (for the meld algorithm implementation, many
additions to the notebook interface, the conversion to pybind and the
Windows port), Fergus Baker (for the new Jupyter kernel), Isuru
Fernando (for the Conda packaging), the Software Sustainability
Institute and the Institute of Advanced Study. Thanks to the many
people who have sent me bug reports (keep 'm coming), and thanks to
all of you who use Cadabra, sent feedback or cited the Cadabra
papers.
Licenses
--------
Cadabra itself is licensed under the GPL-3.0. It includes some dependencies
which have the following licenses:
* tiny-process-lib [https://gitlab.com/eidheim/tiny-process-library/]
MIT license
================================================
FILE: c++lib/.gitignore
================================================
Makefile
treetracker
================================================
FILE: c++lib/CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.12)
set(CMAKE_CXX_STANDARD 17)
project(Cadabra)
#---------------------------------------------------------------------------
# Preamble.
#---------------------------------------------------------------------------
# Set path to additional cmake files
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules")
# Disable warning 'MACOSX_RPATH is enabled by default'
if (POLICY CMP0042)
cmake_policy(SET CMP0042 NEW)
endif(POLICY CMP0042)
# Disable warning 'Only interpret if() arguments as variables or keywords when unquoted'
if (POLICY CMP0054)
cmake_policy(SET CMP0054 NEW)
endif()
# Ensure that we can build the library and install it without having to
# build the samples.
set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY TRUE)
find_package(Python REQUIRED COMPONENTS Interpreter Development)
message(STATUS "Found python library: ${Python_LIBRARIES}")
message(STATUS "Found python headers: ${Python_INCLUDE_DIRS}")
message(STATUS "Python version is ${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.")
#---------------------------------------------------------------------------
# User options.
#---------------------------------------------------------------------------
set(INSTALL_LIB_DIR lib CACHE PATH "Installation directory for library")
set(INSTALL_INCLUDE_DIR include/cadabra2++ CACHE PATH "Installation directory for header files")
# Scalar backend options
option(USE_TREETRACKER "Use the TreeTracker scalar backend" OFF)
set(PATH_TREETRACKER "./treetracker" CACHE STRING "Path to the TreeTracker library")
#---------------------------------------------------------------------------
# Compiler flags.
#---------------------------------------------------------------------------
add_definitions("-DNO_SYMPY")
if(CMAKE_COMPILER_IS_GNUCXX)
add_definitions("-Wall -g -Wno-unused-but-set-variable")
endif()
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14.0)
message(STATUS "This version of g++ (${CMAKE_CXX_COMPILER_VERSION}) incorrectly warns about possibly uninitialised memory when using std::variant containing a std::shared_ptr. Disabling this warning.")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-maybe-uninitialized")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")
endif()
if(MSVC)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(MSVC_FLAGS
"/wd4101" # unreferenced local variable
"/wd4250" # inherits via dominance
"/wd4244" # conversion from x to y, possible loss of data
"/wd4267" # same as 4244
"/wd4996" # deprecated POSIX functions
"-D_CRT_SECURE_NO_WARNINGS" # don't warn about deprecated functions
)
foreach(FLAG ${MSVC_FLAGS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}")
endforeach()
endif()
#---------------------------------------------------------------------------
# Find libraries.
#---------------------------------------------------------------------------
# Locate gmpxx. On Homebrew there seems to be constantly something wrong with
# the pkgconfig for gmpxx. So we just add the include path by hand.
if(APPLE)
add_definitions("-I/usr/local/include -I/opt/local/include")
endif()
if(MSVC)
find_package(GMPXX REQUIRED)
set(GMP_LIB "${GMPXX_LIBRARIES}")
set(GMPXX_LIB "${GMPXX_LIBRARIES}")
include_directories("${VCPKG_INCLUDE_DIRS}")
else()
find_library(GMP_LIB gmp REQUIRED)
find_library(GMPXX_LIB gmpxx REQUIRED)
message(STATUS "Found gmp ${GMP_LIB}")
message(STATUS "Found gmpxx ${GMPXX_LIB}")
endif()
#---------------------------------------------------------------------------
# Enumerate source files.
#---------------------------------------------------------------------------
SET(RESERVED_NODE_SRC_FILES
${CMAKE_CURRENT_SOURCE_DIR}/../core/ReservedNode.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Equals.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Sum.cc
)
set(LOCAL_SRC_FILES
${CMAKE_CURRENT_SOURCE_DIR}/../core/Adjform.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Algorithm.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Cleanup.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Combinatorics.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Compare.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/DisplayBase.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/DisplayTeX.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/DisplaySympy.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/DisplayTerminal.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Grouping.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/TerminalStream.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Multiplier.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/NDSolver.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/NEvaluator.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/NTensor.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/NInterpolatingFunction.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Exceptions.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Exchange.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/ExManip.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Functional.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/IndexIterator.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/IndexClassifier.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Hash.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Kernel.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Parser.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/PreClean.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/PreProcessor.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/ProgressMonitor.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Props.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Stopwatch.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Storage.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/Symbols.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/YoungTab.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Accent.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/AntiCommuting.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/AntiSymmetric.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Commuting.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/CommutingAsProduct.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/CommutingAsSum.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/CommutingBehaviour.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Coordinate.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/DAntiSymmetric.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Depends.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/DependsInherit.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Derivative.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/DerivativeOp.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Determinant.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Diagonal.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/DifferentialForm.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/DiracBar.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Distributable.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/EpsilonTensor.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/ExteriorDerivative.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/FilledTableau.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/GammaMatrix.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/GammaTraceless.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/ImaginaryI.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/ImplicitIndex.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Indices.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Integer.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/InverseMetric.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/KroneckerDelta.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/LaTeXForm.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Matrix.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Metric.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/NonCommuting.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/NumericalFlat.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/PartialDerivative.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/RiemannTensor.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/SatisfiesBianchi.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/SelfAntiCommuting.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/SelfCommuting.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/SelfNonCommuting.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/SortOrder.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Spinor.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Symbol.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Symmetric.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Tableau.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/TableauBase.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/TableauInherit.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/TableauSymmetry.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Trace.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Traceless.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/Weight.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/WeightInherit.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/WeylTensor.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/modules/xperm_new.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/canonicalise.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/collect_components.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/collect_factors.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/collect_terms.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/combine.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/complete.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/decompose_product.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/distribute.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/drop_weight.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/einsteinify.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/eliminate_kronecker.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/eliminate_metric.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/epsilon_to_delta.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/evaluate.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/expand.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/expand_delta.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/expand_diracbar.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/expand_power.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/factor_in.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/factor_out.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/fierz.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/first_order_form.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/flatten_product.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/flatten_sum.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/indexsort.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/integrate_by_parts.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/join_gamma.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/keep_terms.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/lr_tensor.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/order.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/product_rule.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/reduce_delta.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/rename_dummies.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/rewrite_indices.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/simplify.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/sort_product.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/sort_sum.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/split_gamma.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/split_index.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/substitute.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/sym.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/tab_basics.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/take_match.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/replace_match.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/unwrap.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/vary.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/young_project.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/young_project_product.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/young_project_tensor.cc
${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/meld.cc
SympyDummy.cc
)
set(MAIN_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/../core/Adjform.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/Algorithm.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/Combinatorics.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/Compare.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/DisplayBase.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/DisplayTerminal.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/NEvaluator.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/Equals.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/Exceptions.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/ExManip.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/IndexClassifier.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/IndexIterator.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/Kernel.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/lru_cache.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/Hash.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/Multiplier.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/NTensor.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/Parser.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/ProgressMonitor.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/Props.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/ReservedNode.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/Storage.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/Stopwatch.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/Sum.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/TerminalStream.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/tree.hh
${CMAKE_CURRENT_SOURCE_DIR}/../core/YoungTab.hh
)
FILE(GLOB ALGO_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../core/algorithms/*.hh)
FILE(GLOB PROP_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../core/properties/*.hh)
# Copy relevant header files to the binary directory
file(COPY ${MAIN_HEADERS} DESTINATION ${CMAKE_BINARY_DIR}/include/cadabra2++)
file(COPY ${ALGO_HEADERS} DESTINATION ${CMAKE_BINARY_DIR}/include/cadabra2++/algorithms)
file(COPY ${PROP_HEADERS} DESTINATION ${CMAKE_BINARY_DIR}/include/cadabra2++/properties)
# Create a 'master' header which includes all the above for convenience
file(WRITE "${CMAKE_BINARY_DIR}/include/cadabra2++.hh" "// Main headers\n")
foreach(HEADER ${MAIN_HEADERS})
get_filename_component(FILENAME "${HEADER}" NAME)
file(APPEND "${CMAKE_BINARY_DIR}/include/cadabra2++.hh" "#include \"cadabra2++/${FILENAME}\"\n")
endforeach()
file(APPEND "${CMAKE_BINARY_DIR}/include/cadabra2++.hh" "\n// Properties\n")
foreach(HEADER ${PROP_HEADERS})
get_filename_component(FILENAME "${HEADER}" NAME)
file(APPEND "${CMAKE_BINARY_DIR}/include/cadabra2++.hh" "#include \"cadabra2++/properties/${FILENAME}\"\n")
endforeach()
file(APPEND "${CMAKE_BINARY_DIR}/include/cadabra2++.hh" "\n// Algorithms\n")
foreach(HEADER ${ALGO_HEADERS})
get_filename_component(FILENAME "${HEADER}" NAME)
file(APPEND "${CMAKE_BINARY_DIR}/include/cadabra2++.hh" "#include \"cadabra2++/algorithms/${FILENAME}\"\n")
endforeach()
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/cpplib.hh.in CPPLIB_HH_IN)
file(APPEND "${CMAKE_BINARY_DIR}/include/cadabra2++.hh" "\n//Helper functions\n${CPPLIB_HH_IN}")
set(LIB_INCLUDE_DIRS
"."
"${CADABRA_CORE_DIR}"
"${CADABRA_LIBS_DIR}/pybind11/include"
"${CADABRA_LIBS_DIR}/internal/include"
"${CADABRA_LIBS_DIR}/dbg"
${Python_INCLUDE_DIRS}
)
message("${LIB_INCLUDE_DIRS}")
if(USE_TREETRACKER)
add_definitions(-DUSE_TREETRACKER)
if(EXISTS "${PATH_TREETRACKER}/treetracker.h" OR EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${PATH_TREETRACKER}/treetracker.h")
include_directories(${PATH_TREETRACKER})
message("-- Using the TreeTracker scalar backend at ${PATH_TREETRACKER}")
else()
message(FATAL_ERROR "-- Cannot find the 'treetracker.h' file in '${PATH_TREETRACKER}'")
endif()
else()
message("-- Not using the TreeTracker scalar backend")
endif()
#---------------------------------------------------------------------------
# Targets
#---------------------------------------------------------------------------
add_library(cadabra2++objects OBJECT ${LOCAL_SRC_FILES} ${RESERVED_NODE_SRC_FILES})
set_property(TARGET cadabra2++objects PROPERTY POSITION_INDEPENDENT_CODE 1)
target_include_directories(cadabra2++objects PUBLIC "${LIB_INCLUDE_DIRS}")
add_library(cadabra2++ SHARED $)
set_target_properties(cadabra2++ PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADER_FILES}")
target_link_libraries(cadabra2++ ${GMPXX_LIB} ${GMP_LIB} ${Python_LIBRARIES})
add_library(cadabra2++_static STATIC $)
target_link_libraries(cadabra2++_static ${GMPXX_LIB} ${GMP_LIB} ${Python_LIBRARIES})
if(TBB_FOUND)
target_link_libraries(cadabra2++ TBB::tbb)
target_compile_definitions(cadabra2++objects PRIVATE HAS_TBB)
endif()
# Trivial example
add_executable(trivial trivial.cc)
target_include_directories(trivial PUBLIC
"${CADABRA_LIBS_DIR}/pybind11/include"
"${CMAKE_BINARY_DIR}/include"
"${CMAKE_BINARY_DIR}/include/cadabra2++"
${Python_INCLUDE_DIRS}
)
target_link_libraries(trivial cadabra2++)
# Sample executable
add_executable(simple simple.cc)
target_include_directories(simple PUBLIC
"${CADABRA_LIBS_DIR}/pybind11/include"
"${CMAKE_BINARY_DIR}/include"
"${CMAKE_BINARY_DIR}/include/cadabra2++"
${Python_INCLUDE_DIRS}
)
target_link_libraries(simple cadabra2++)
# Test for adjform
add_executable(adjform adjform.cc)
target_include_directories(adjform PUBLIC
"${CADABRA_LIBS_DIR}/pybind11/include"
"${CMAKE_BINARY_DIR}/include"
"${CMAKE_BINARY_DIR}/include/cadabra2++"
${Python_INCLUDE_DIRS}
)
target_link_libraries(adjform cadabra2++)
# Test for numerical evaluation
add_executable(nevaluate nevaluate.cc)
target_include_directories(nevaluate PUBLIC
"${CADABRA_LIBS_DIR}/pybind11/include"
"${CMAKE_BINARY_DIR}/include"
"${CMAKE_BINARY_DIR}/include/cadabra2++"
${Python_INCLUDE_DIRS}
)
target_link_libraries(nevaluate cadabra2++)
#---------------------------------------------------------------------------
# Installation
#---------------------------------------------------------------------------
install(TARGETS cadabra2++ LIBRARY DESTINATION "${INSTALL_LIB_DIR}")
install(TARGETS cadabra2++_static LIBRARY DESTINATION "${INSTALL_LIB_DIR}" ARCHIVE DESTINATION "${INSTALL_LIB_DIR}")
install(DIRECTORY ${CMAKE_BINARY_DIR}/include DESTINATION "${INSTALL_INCLUDE_DIR}")
install(DIRECTORY
DESTINATION bin
DIRECTORY_PERMISSIONS
OWNER_READ
OWNER_WRITE
OWNER_EXECUTE
GROUP_READ
GROUP_EXECUTE
WORLD_READ
WORLD_EXECUTE
)
install(DIRECTORY
DESTINATION lib
DIRECTORY_PERMISSIONS
OWNER_READ
OWNER_WRITE
OWNER_EXECUTE
GROUP_READ
GROUP_EXECUTE
WORLD_READ
WORLD_EXECUTE
)
================================================
FILE: c++lib/README.txt
================================================
This directory contains code to illustrate the use of Cadabra from
within C++ programs.
================================================
FILE: c++lib/SympyDummy.cc
================================================
#include "Parser.hh"
#include "Cleanup.hh"
#include "PreClean.hh"
#include "SympyCdb.hh"
#include "DisplaySympy.hh"
#ifdef USE_TREETRACKER
#include "treetracker.h"
#endif
#include
cadabra::Ex::iterator sympy::apply(const cadabra::Kernel& kernel, cadabra::Ex& ex, cadabra::Ex::iterator& it,
const std::vector& head, std::vector args,
const std::string& method)
{
std::ostringstream str;
if(head.size()>0)
str << head[0] << "(";
cadabra::DisplaySympy ds(kernel, ex);
ds.output(str, it);
if(head.size()>0)
if(args.size()>0)
str << ", " << args[0] << ")";
str << method;
if(head.size()>0)
str << ")";
// std::cerr << "Send: " << str.str() << std::endl;
#ifdef USE_TREETRACKER
auto res = TreeTracker::FromString(str.str());
res.RecursiveSimplify();
std::stringstream istr;
res.ShowTree(istr, 0, false, true);
// std::cerr << "Return: " << istr.str() << std::endl;
auto ptr = std::make_shared();
cadabra::Parser parser(ptr);
istr >> parser;
pre_clean_dispatch_deep(kernel, *parser.tree);
cleanup_dispatch_deep(kernel, *parser.tree);
//parser.tree->print_recursive_treeform(std::cerr, parser.tree->begin());
ds.import(*parser.tree);
cadabra::Ex::iterator first=parser.tree->begin();
it = ex.move_ontop(it, first);
#endif
return it;
}
void sympy::invert_matrix(const cadabra::Kernel& kernel, cadabra::Ex& ex,
cadabra::Ex& rules, const cadabra::Ex& tocompute)
{
throw std::logic_error("Not implemented: sympy::invert_matrix");
}
void sympy::determinant(const cadabra::Kernel&, cadabra::Ex& ex, cadabra::Ex& rules, const cadabra::Ex& tocompute)
{
throw std::logic_error("Not implemented: sympy::determinant");
}
void sympy::trace(const cadabra::Kernel&, cadabra::Ex& ex, cadabra::Ex& rules, const cadabra::Ex& tocompute)
{
throw std::logic_error("Not implemented: sympy::trace");
}
================================================
FILE: c++lib/adjform.cc
================================================
#include "cadabra2++.hh"
#include
using namespace cadabra;
using namespace cadabra::cpplib;
int main(int, char**)
{
Kernel k(true);
pprint_enable_utf8();
//{\mu,\nu}::Indices(vector).
//tr{#}::Trace.
//u^{\mu}::SelfNonCommuting.
//u^{\mu}::ImplicitIndex.
//ex:=tr{A u^{\nu} u^{\mu} u^{\mu} u^{\nu} + B u^{\mu} u^{\mu} u^{\nu} u^{\nu}}:
//meld(_);
inject_property(k, "{A,B,C,D }");
inject_property(k, "tr{#}");
auto ex = R"(tr(A B C D + B C D A)"_ex(k);
meld m(k, *ex);
std::cout << pprint(k, ex) << '\n';
m.apply_pre_order();
std::cout << pprint(k, ex) << '\n';
// assert ex == $2 * Tr{ A B C D }$
}
================================================
FILE: c++lib/cpplib.hh.in
================================================
#ifdef _MSC_VER
#define WIN32_LEAN_AND_MEAN
#include
#endif
namespace cadabra {
namespace cpplib {
using ExPtr = std::shared_ptr;
namespace detail {
struct ExConstructorProxy
{
ExConstructorProxy(const char* data) : data(data) {}
ExPtr operator () (Kernel& kernel)
{
return kernel.ex_from_string(data);
}
std::string data;
};
struct ExPrettyPrinter
{
ExPrettyPrinter(const Kernel& kernel, Ex::iterator it, bool use_unicode = true)
: kernel(kernel)
, ex(it)
, use_unicode(use_unicode) {}
const Kernel& kernel;
Ex ex;
bool use_unicode;
};
}
ExPtr copy(const ExPtr& ex)
{
return std::make_shared(*ex);
}
template
void inject_property(Kernel& kernel, const ExPtr& pattern, const ExPtr& args = nullptr)
{
kernel.inject_property(new Prop(), pattern, args);
}
template
void inject_property(Kernel& kernel, const char* pattern, const ExPtr& args = nullptr)
{
inject_property(kernel, kernel.ex_from_string(pattern), args);
}
template
void inject_property(Kernel& kernel, const ExPtr& pattern, const char* args)
{
inject_property(kernel, pattern, kernel.ex_from_string(args));
}
template
void inject_property(Kernel& kernel, const char* pattern, const char* args)
{
inject_property(kernel, kernel.ex_from_string(pattern), kernel.ex_from_string(args));
}
template
ExPtr apply(const Kernel& kernel, const ExPtr& ex, Args&&... args, bool deep = true, bool repeat = false, unsigned int depth = 0)
{
Algo algo(kernel, *ex, std::forward(args)...);
algo.apply_generic(deep, repeat, depth);
return ex;
}
template
ExPtr apply(const Kernel& kernel, const ExPtr& ex, Ex::iterator it, Args&&... args, bool deep = true, bool repeat = false, unsigned int depth = 0)
{
Algo algo(kernel, *ex, std::forward(args)...);
algo.apply_generic(it, deep, repeat, depth);
return ex;
}
template
ExPtr apply_preorder(const Kernel& kernel, const ExPtr& ex, Args&&... args)
{
Algo algo(kernel, *ex, std::forward(args)...);
algo.apply_pre_order();
return ex;
}
inline void pprint_enable_utf8()
{
// Set the codepage on windows to utf8
#ifdef _MSC_VER
SetConsoleOutputCP(65001);
#endif
}
inline detail::ExConstructorProxy operator "" _ex(const char* ex, std::size_t n)
{
return detail::ExConstructorProxy(ex);
}
inline detail::ExPrettyPrinter pprint(const Kernel& kernel, const ExPtr& ex, bool use_unicode = true)
{
return detail::ExPrettyPrinter(kernel, ex->begin(), use_unicode);
}
inline detail::ExPrettyPrinter pprint(const Kernel& kernel, const Ex& ex, bool use_unicode = true)
{
return detail::ExPrettyPrinter(kernel, ex.begin(), use_unicode);
}
inline detail::ExPrettyPrinter pprint(const Kernel& kernel, Ex::iterator it, bool use_unicode = true)
{
return detail::ExPrettyPrinter(kernel, it, use_unicode);
}
inline std::ostream& operator << (std::ostream& stream, const detail::ExPrettyPrinter& pretty_printer)
{
DisplayTerminal dt(pretty_printer.kernel, pretty_printer.ex, pretty_printer.use_unicode);
dt.output(stream);
return stream;
}
}
}
================================================
FILE: c++lib/nevaluate.cc
================================================
#include "cadabra2++.hh"
#include
using namespace cadabra;
using namespace cadabra::cpplib;
int main(int, char **)
{
Kernel k(true);
pprint_enable_utf8();
//{\mu,\nu}::Indices(vector).
// tr{#}::Trace.
// u^{\mu}::SelfNonCommuting.
// u^{\mu}::ImplicitIndex.
// ex:=tr{A u^{\nu} u^{\mu} u^{\mu} u^{\nu} + B u^{\mu} u^{\mu} u^{\nu}
// u^{\nu}}: meld(_);
// inject_property(k, "{A,B,C,D }");
// inject_property(k, "tr{#}");
// Scalar broadcast
NTensor t3a( { 2.0 } );
std::cerr << "t3a.shape.size() = " << t3a.shape.size() << std::endl;
std::cerr << "t3a.shape[0] = " << t3a.shape[0] << std::endl;
NTensor t432a=t3a.broadcast( {4, 1}, 1 );
std::cerr << t432a << std::endl;
// NTensor broadcast
NTensor t3( { 1.0, 2.0, 3.0 } );
NTensor t432=t3.broadcast( {4,3,2}, 1 );
std::cerr << t432 << std::endl;
NTensor t3c( { 1.0, 2.0, 3.0 } );
NTensor t432c1=t3c.broadcast( {3,3,2}, 1 );
std::cerr << t432c1 << std::endl;
NTensor t432c2=t3c.broadcast( {3,3,2}, 0 );
std::cerr << t432c2 << std::endl;
// Multiplying two scalar variables which each take
// an array of values leads to an outer product.
auto ex1b = "B*A + A"_ex(k);
NEvaluator ev1b(ex1b->begin());
ev1b.set_variable(Ex("A"), NTensor({1.0, 2.0, 3.0}));
ev1b.set_variable(Ex("B"), NTensor({0.5, 1.0, 5.0, 10.0}));
// This should give a {3, 4} tensor.
auto res1b = ev1b.evaluate();
std::cout << "B*A + A = " << res1b << "\n\n";
auto ex1 = "B*A + C"_ex(k);
NEvaluator ev1(ex1->begin());
ev1.set_variable(Ex("A"), NTensor({1.0, 2.0, 3.0}));
ev1.set_variable(Ex("B"), NTensor({0.5, 1.0, 5.0, 10.0}));
ev1.set_variable(Ex("C"), NTensor({1.0, -1.0}));
// This should give a {3, 4, 2} tensor.
auto res1 = ev1.evaluate();
std::cout << "B*A + C = " << res1 << "\n\n";
// Trigonometric functions.
auto ex2 = R"( A + B \cos( C ) )"_ex(k);
std::cout << pprint(k, ex2) << '\n';
NTensor nt2({2,4}, 0.0);
nt2.at({1,2}) = 3.1415;
for(auto& v: nt2.values)
std::cout << v << ", ";
std::cout << "\n\n";
std::cout << nt2 << std::endl;
NEvaluator ev(ex2->begin());
ev.set_variable(Ex("C"), { 3.0 });
ev.set_variable(Ex("B"), { 2.3 });
ev.set_variable(Ex("A"), { 1.2 });
auto res2 = ev.evaluate();
std::cout << "A + B cos(C) = " << res2 << "\n\n";
// Double trig.
Stopwatch sw;
auto ex3 = R"( \cos(x) \sin(y) )"_ex(k);
NEvaluator ev3(ex3->begin());
ev3.set_variable(Ex("x"), NTensor::linspace(0.0, 3.14, 1000));
ev3.set_variable(Ex("y"), NTensor::linspace(0.0, 3.14, 1000));
sw.start();
int num=100;
for(int i=0; i
#include
/// \file simple.cc
/// \ingroup libcadabra
///
/// Sample program to demonstrate the use of Cadabra directly from C++ code.
void test1()
{
// The following few lines are equivalent to entering
//
// {r,t}::Coordinate.
// {m,n}::Indices(values={t,r}, position=free).
// ex:= A_{m} A^{m};
// rl:= A_{t} = 3 + a;
// evaluate(ex, rl);
//
// in the Cadabra notebook.
cadabra::Kernel kernel;
kernel.inject_property(new cadabra::Coordinate(), kernel.ex_from_string("{r,t}"), 0);
kernel.inject_property(new cadabra::Indices(), kernel.ex_from_string("{m,n}"),
kernel.ex_from_string("values={t,r}, position=free"));
auto ex = kernel.ex_from_string("A_{m} A^{m}");
auto rl = kernel.ex_from_string("A_{t} = 3 + a ");
cadabra::evaluate ev(kernel, *ex, *rl);
ev.apply_generic();
// Pretty-printing stream object.
cadabra::TerminalStream ss(kernel, std::cerr);
ss << ex << std::endl;
}
void test2()
{
// The following few lines are equivalent to entering
//
// {m,n,p,q}::Indices(position=free).
// \partial{#}::PartialDerivative;
// ex:= \int{ F_{m n} F^{m n} }{x};
// rl:= F_{m n} = \\partial_{m}{A_{n}} - \\partial_{n}{A_{m}};
// substitute(ex, rl, deep=True);
//
// in the Cadabra notebook.
cadabra::Kernel kernel;
auto ind1 = kernel.ex_from_string("{m,n,p,q}");
auto ind2 = kernel.ex_from_string("position=free");
kernel.inject_property(new cadabra::Indices(), ind1, ind2);
auto pd = kernel.ex_from_string("\\partial{#}");
kernel.inject_property(new cadabra::PartialDerivative(), pd, 0);
auto ex = kernel.ex_from_string("\\int{ F_{m n} F^{m n} }{x}");
auto rl = kernel.ex_from_string("F_{m n} = \\partial_{m}{A_{n}} - \\partial_{n}{A_{m}}");
// Pretty-printing stream object.
cadabra::TerminalStream ss(kernel, std::cerr);
ss << ex << std::endl;
ss << rl << std::endl;
// Apply the 'substitute' algorithm.
cadabra::substitute subs(kernel, *ex, *rl);
subs.apply_generic();
ss << ex << std::endl;
}
int main(int argc, char **argv)
{
test1();
test2();
}
================================================
FILE: c++lib/trivial.cc
================================================
#include "cadabra2++.hh"
#include
using namespace cadabra;
using namespace cadabra::cpplib;
int main() {
Kernel k(true);
inject_property(k, "{A,B}");
auto ex = "A B - B A"_ex(k);
sort_product sp(k, *ex);
sp.apply_generic();
collect_terms ct(k, *ex);
ct.apply_generic();
std::cout << pprint(k, ex) << std::endl;
}
================================================
FILE: client_server/Actions.cc
================================================
#include "Actions.hh"
#include "DataCell.hh"
#include "DocumentThread.hh"
#include "GUIBase.hh"
#include
#include
#include
using namespace cadabra;
#define DEBUG(ln)
// #define DEBUG(ln) ln
ActionBase::ActionBase(DataCell::id_t id)
: ref_id(id)
{
}
bool ActionBase::undoable() const
{
return true;
}
void ActionBase::execute(DocumentThread& cl, GUIBase& )
{
if(ref_id.id==0) {
// A zero ID means the current cell.
ref = cl.current_cell;
return;
}
else {
// Lookup the cell with the given ID.
auto it=cl.doc.begin();
while(it!=cl.doc.end()) {
if((*it).id().id==ref_id.id) {
ref=it;
return;
}
++it;
}
}
// Not found, throw exception.
std::string class_name = boost::core::demangle(typeid(*this).name());
throw std::logic_error(class_name + ": cannot find cell with id "+std::to_string(ref_id.id));
}
ActionAddCell::ActionAddCell(DataCell cell, DataCell::id_t ref_id, Position pos_, bool activate_)
: ActionBase(ref_id)
, newcell(cell)
, pos(pos_)
, activate(activate_)
, is_replacement(false)
, is_input_form(false)
{
}
void ActionAddCell::execute(DocumentThread& cl, GUIBase& gb)
{
ActionBase::execute(cl, gb);
// Insert this DataCell into the DTree document. We first need
// to figure out whether we already have a cell with the DataCell's
// cell_id; in this case we have to replace, not append/insert.
auto it=cl.doc.begin();
while(it!=cl.doc.end()) {
if((*it).id().id==newcell.id().id) {
// FIXME: right now we only change textbuf.
DEBUG( std::cerr << "found! " << it->id().id << ", " << static_cast(it->cell_type) << std::endl; )
it->textbuf=newcell.textbuf;
gb.update_cell(cl.doc, it);
is_replacement=true;
return;
}
++it;
}
// If we get here we have to append/insert.
DEBUG( std::cerr << "ActionAddCell::execute: add cell with id " << newcell.id().id; )
switch(pos) {
case Position::before:
newref = cl.doc.insert(ref, newcell);
DEBUG( std::cerr << " before "; )
break;
case Position::after:
newref = cl.doc.insert_after(ref, newcell);
DEBUG( std::cerr << " after "; )
break;
case Position::child:
newref = cl.doc.append_child(ref, newcell);
DEBUG( std::cerr << " as child of "; )
break;
}
DEBUG( std::cerr << " " << ref->id().id << std::endl; )
child_num=cl.doc.index(newref);
DEBUG( std::cerr << "ActionAddCell::execute: added as child " << child_num <<
": |" << newcell.textbuf << "|" << std::endl; )
gb.add_cell(cl.doc, newref, true);
if(activate)
gb.position_cursor(cl.doc, newref, -1);
if(newcell.cell_type == DataCell::CellType::input_form)
is_input_form=true;
}
void ActionAddCell::revert(DocumentThread& cl, GUIBase& gb)
{
// Remove the GUI cell from the notebook and then
// remove the corresponding DataCell from the DTree.
DEBUG( std::cerr << "ActionAddCell::revert: removing child " << child_num << std::endl; )
DTree::sibling_iterator ch;
switch(pos) {
case Position::before:
// `ref` is a cell after our cell.
ch = cl.doc.child(cl.doc.parent(ref), child_num);
break;
case Position::after:
// `ref` is a cell before our cell.
ch = cl.doc.child(cl.doc.parent(ref), child_num);
break;
case Position::child:
// `ref` is a parent of our cell.
ch = cl.doc.child(ref, child_num);
break;
}
DEBUG( std::cerr << "ActionAddCell::revert: removing cell " << ch->textbuf << std::endl; )
gb.remove_cell(cl.doc, ch);
// std::cerr << "ActionAddCell::revert: finally erase datacell" << std::endl;
cl.doc.erase(ch);
}
bool ActionAddCell::undoable() const
{
return !(is_replacement || is_input_form);
}
ActionPositionCursor::ActionPositionCursor(DataCell::id_t ref_id, Position pos_)
: ActionBase(ref_id), needed_new_cell_with_id(0), pos(pos_)
{
}
void ActionPositionCursor::execute(DocumentThread& cl, GUIBase& gb)
{
ActionBase::execute(cl, gb);
switch(pos) {
case Position::in:
// std::cerr << "in" << std::endl;
newref = ref;
break;
case Position::next: {
DTree::sibling_iterator sib=ref;
bool found=false;
while(cl.doc.is_valid(++sib)) {
if(sib->cell_type==DataCell::CellType::python || sib->cell_type==DataCell::CellType::latex) {
if(!sib->hidden) {
newref=sib;
found=true;
break;
}
}
}
if(!found) {
if(ref->textbuf=="") { // If the last cell is empty, stay where we are.
newref=ref;
}
else {
// Make sure that we store the generated cell id so we
// can re-use it if we execute this in redo.
if(needed_new_cell_with_id > 0) {
DataCell::id_t id;
id.id = needed_new_cell_with_id;
DataCell newcell(id, DataCell::CellType::python, "");
newref = cl.doc.insert(sib, newcell);
}
else {
DataCell newcell(DataCell::CellType::python, "");
needed_new_cell_with_id=newcell.id().id;
newref = cl.doc.insert(sib, newcell);
}
}
}
break;
}
case Position::previous: {
bool found=false;
DTree::sibling_iterator sib=ref;
while(cl.doc.is_valid(--sib)) {
if(sib->cell_type==DataCell::CellType::python || sib->cell_type==DataCell::CellType::latex) {
if(!sib->hidden) {
newref=sib;
found=true;
break;
}
}
}
if(!found)
newref=ref; // No previous sibling cell. FIXME: walk tree structure
break;
}
}
// Update GUI.
if(needed_new_cell_with_id > 0) {
// std::cerr << "cadabra-client: adding new visual cell before positioning cursor" << std::endl;
gb.add_cell(cl.doc, newref, true);
}
// std::cerr << "cadabra-client: positioning cursor" << std::endl;
gb.position_cursor(cl.doc, newref, -1);
DEBUG( std::cerr << "ActionPositionCursor::execute: done" << std::endl; )
}
void ActionPositionCursor::revert(DocumentThread& cl, GUIBase& gb)
{
if(needed_new_cell_with_id > 0) {
gb.remove_cell(cl.doc, newref);
cl.doc.erase(newref);
}
gb.position_cursor(cl.doc, ref, -1);
DEBUG( std::cerr << "ActionPositionCursor::revert: done" << std::endl; )
}
ActionRemoveCell::ActionRemoveCell(DataCell::id_t ref_id)
: ActionBase(ref_id)
{
}
ActionRemoveCell::~ActionRemoveCell()
{
}
void ActionRemoveCell::execute(DocumentThread& cl, GUIBase& gb)
{
ActionBase::execute(cl, gb);
gb.remove_cell(cl.doc, ref);
reference_parent_cell = cl.doc.parent(ref);
reference_child_index = cl.doc.index(ref);
removed_tree=DTree(ref);
DEBUG( std::cerr << "removed has " << cl.doc.number_of_children(ref) << " children" << std::endl; )
cl.doc.erase(ref);
}
void ActionRemoveCell::revert(DocumentThread& cl, GUIBase& gb)
{
DEBUG( std::cerr << "need to undo a remove cell at index " << reference_child_index << std::endl; )
DTree::iterator newcell;
if(cl.doc.number_of_children(reference_parent_cell)==0) {
newcell = cl.doc.append_child(reference_parent_cell, removed_tree.begin());
}
else {
auto it = cl.doc.child(reference_parent_cell, reference_child_index);
// ++it;
newcell = cl.doc.insert_subtree(it, removed_tree.begin());
DEBUG( std::cerr << "added doc cell " << newcell->textbuf << " at " << &(*newcell) << " before " << it->textbuf << std::endl; )
}
gb.add_cell(cl.doc, newcell, true);
DEBUG( std::cerr << "added vis rep" << std::endl; )
}
ActionReplaceCell::ActionReplaceCell(DataCell::id_t ref_id)
: ActionBase(ref_id)
{
}
ActionReplaceCell::~ActionReplaceCell()
{
}
void ActionReplaceCell::execute(DocumentThread& cl, GUIBase& gb)
{
}
void ActionReplaceCell::revert(DocumentThread& cl, GUIBase& gb)
{
}
bool ActionReplaceCell::undoable() const
{
return false;
}
ActionSplitCell::ActionSplitCell(DataCell::id_t ref_id)
: ActionBase(ref_id)
{
}
ActionSplitCell::~ActionSplitCell()
{
}
void ActionSplitCell::execute(DocumentThread& cl, GUIBase& gb)
{
ActionBase::execute(cl, gb);
size_t pos = gb.get_cursor_position(cl.doc, ref);
std::string segment1=ref->textbuf.substr(0, pos);
std::string segment2=ref->textbuf.substr(pos);
// Strip leading newline in 2nd segment, if any.
if(segment2.size()>0) {
if(segment2[0]=='\n')
segment2=segment2.substr(1);
}
DataCell newcell(ref->cell_type, segment2);
newref = cl.doc.insert_after(ref, newcell);
ref->textbuf=segment1;
gb.add_cell(cl.doc, newref, true);
gb.update_cell(cl.doc, ref);
}
void ActionSplitCell::revert(DocumentThread&, GUIBase& )
{
// FIXME: implement
}
ActionSetRunStatus::ActionSetRunStatus(DataCell::id_t ref_id, bool running)
: ActionBase(ref_id), new_running_(running)
{
}
bool ActionSetRunStatus::undoable() const
{
return false;
}
void ActionSetRunStatus::execute(DocumentThread& cl, GUIBase& gb)
{
ActionBase::execute(cl, gb);
gb.update_cell(cl.doc, ref);
was_running_=ref->running;
ref->running=new_running_;
}
void ActionSetRunStatus::revert(DocumentThread&, GUIBase& )
{
}
ActionRunCell::ActionRunCell(DataCell::id_t ref_id)
: ActionBase(ref_id), run_all_cells(false)
{
}
ActionRunCell::ActionRunCell()
: ActionBase(DataCell::id_t()), run_all_cells(true)
{
}
ActionRunCell::~ActionRunCell()
{
}
bool ActionRunCell::undoable() const
{
return false;
}
void ActionRunCell::execute(DocumentThread& cl, GUIBase& gb)
{
if(!run_all_cells) {
ActionBase::execute(cl, gb);
cl.run_cell(ref, false);
}
else {
cl.run_all_cells();
}
}
void ActionRunCell::revert(DocumentThread&, GUIBase& )
{
}
ActionOpen::ActionOpen(const std::string& n)
: ActionBase(DataCell::id_t()), notebook_name(n)
{
}
ActionOpen::~ActionOpen()
{
}
bool ActionOpen::undoable() const
{
return false;
}
void ActionOpen::execute(DocumentThread& cl, GUIBase& gb)
{
std::ifstream file(notebook_name);
std::string content, line;
while(std::getline(file, line))
content+=line;
cl.load_from_string(content);
}
void ActionOpen::revert(DocumentThread&, GUIBase& )
{
}
ActionSetVariableList::ActionSetVariableList(DataCell::id_t ref_id, std::set variables)
: ActionBase(ref_id), new_variables_(variables)
{
}
bool ActionSetVariableList::undoable() const
{
return false;
}
void ActionSetVariableList::execute(DocumentThread& cl, GUIBase& gb)
{
ActionBase::execute(cl, gb);
ref->variables_referenced=new_variables_;
}
void ActionSetVariableList::revert(DocumentThread&, GUIBase& )
{
}
ActionInsertText::ActionInsertText(DataCell::id_t ref_id, int pos, const std::string& content)
: ActionBase(ref_id), insert_pos(pos), text(content)
{
}
void ActionInsertText::execute(DocumentThread& cl, GUIBase& gb)
{
ActionBase::execute(cl, gb);
ref->textbuf.insert(insert_pos, text);
DEBUG( std::cerr << "ActionInsertText::execute: textbuf now |" << ref->textbuf << "|" << std::endl; )
gb.update_cell(cl.doc, ref);
}
void ActionInsertText::revert(DocumentThread& cl, GUIBase& gb)
{
ref->textbuf.erase(insert_pos, text.size());
DEBUG( std::cerr << "ActionInsertText::revert: textbuf now |" << ref->textbuf << "|" << std::endl; )
gb.update_cell(cl.doc, ref);
}
ActionCompleteText::ActionCompleteText(DataCell::id_t ref_id, int pos, const std::string& content, int alt)
: ActionBase(ref_id), insert_pos(pos), text(content), alternative_(alt)
{
}
void ActionCompleteText::execute(DocumentThread& cl, GUIBase& gb)
{
ActionBase::execute(cl, gb);
auto endpos = ref->textbuf.insert(insert_pos, text);
// std::cerr << "complete: textbuf now |" << ref->textbuf << "|" << std::endl;
gb.update_cell(cl.doc, ref);
gb.position_cursor(cl.doc, ref, insert_pos+text.size());
}
void ActionCompleteText::revert(DocumentThread& cl, GUIBase& gb)
{
ref->textbuf.erase(insert_pos, text.size());
gb.update_cell(cl.doc, ref);
gb.position_cursor(cl.doc, ref, insert_pos);
}
int ActionCompleteText::length() const
{
return text.size();
}
int ActionCompleteText::alternative() const
{
return alternative_;
}
ActionEraseText::ActionEraseText(DataCell::id_t ref_id, int start, int end)
: ActionBase(ref_id), from_pos(start), to_pos(end)
{
}
void ActionEraseText::execute(DocumentThread& cl, GUIBase& gb)
{
ActionBase::execute(cl, gb);
DEBUG( std::cerr << from_pos << ", " << to_pos << std::endl; )
removed_text=ref->textbuf.substr(from_pos, to_pos-from_pos);
ref->textbuf.erase(from_pos, to_pos-from_pos);
}
void ActionEraseText::revert(DocumentThread& cl, GUIBase& gb)
{
ref->textbuf.insert(from_pos, removed_text);
gb.update_cell(cl.doc, ref);
gb.position_cursor(cl.doc, ref, from_pos+removed_text.size());
}
================================================
FILE: client_server/Actions.hh
================================================
#pragma once
#include "DataCell.hh"
#include "DocumentThread.hh"
#include
namespace cadabra {
class DocumentThread;
class GUIBase;
/// \ingroup clientserver
///
/// All actions derive from the ActionBase object, which defines
/// the interface they need to implement. These objects are used to
/// pass (user) action instructions around. They can be stored in
/// undo/redo stacks. All actions run on the GUI thread. The
/// update_gui members typically call members of the GUIBase class.
/// Action objects are allowed to modify the DTree document doc,
/// since they essentially contain code which is part of the
/// DocumentThread object.
///
/// All modifications to the document are done by calling 'perform' with an
/// action object. This enables us to implement an undo stack. This method
/// will take care of making the actual change to the DTree document, and
/// call back on the 'change' methods above to inform the derived class
/// that a change has been made.
class ActionBase {
public:
ActionBase(DataCell::id_t ref_id);
/// Perform the action. This should update both the document
/// tree data structure and the GUI. The latter is updated
/// by calling relevant methods on the GUIBase object passed
/// in.
///
/// The base class just looks up the cell given its `id_t`.
/// If your action does not refer to a cell at all, you do
/// not need to call the base class `execute`.
virtual void execute(DocumentThread&, GUIBase&);
/// Revert the change to the DTree document and the GUI.
virtual void revert(DocumentThread&, GUIBase&)=0;
/// Can this action be undone?
virtual bool undoable() const;
DataCell::id_t ref_id;
/// If you want a callback once this action has finished,
/// set it here before queuing.
std::function callback;
protected:
DTree::iterator ref;
};
/// \ingroup clientserver
///
/// Add a cell to the notebook.
class ActionAddCell : public ActionBase {
public:
enum class Position { before, after, child };
ActionAddCell(DataCell, DataCell::id_t ref_, Position pos_, bool activate=false);
virtual ~ActionAddCell() {};
virtual void execute(DocumentThread&, GUIBase&) override;
virtual void revert(DocumentThread&, GUIBase&) override;
/// Can this action be undone?
virtual bool undoable() const override;
private:
// Keep track of the location where this cell is inserted into
// the notebook.
DataCell newcell;
DTree::iterator newref;
Position pos;
int child_num;
bool activate;
// If we are replacing a cell, keep track of that so we
// report that we are not undoable.
bool is_replacement;
// For input-form cells, we want no undo, as they will go
// when the owner cell will be reverted.
bool is_input_form;
};
/// \ingroup clientserver
///
/// Position the cursor relative to the indicated cell. If position is 'next' and
/// there is no input cell following the indicated one, create a new one.
class ActionPositionCursor : public ActionBase {
public:
enum class Position { in, next, previous };
ActionPositionCursor(DataCell::id_t ref_id_, Position pos_);
virtual ~ActionPositionCursor() {};
virtual void execute(DocumentThread&, GUIBase&) override;
virtual void revert(DocumentThread&, GUIBase&) override;
private:
uint64_t needed_new_cell_with_id;
DTree::iterator newref;
Position pos;
};
/// \ingroup clientserver
///
/// Update the running status of the indicated cell.
class ActionSetRunStatus : public ActionBase {
public:
ActionSetRunStatus(DataCell::id_t ref_id_, bool running);
virtual ~ActionSetRunStatus() {};
virtual void execute(DocumentThread&, GUIBase&) override;
virtual void revert(DocumentThread&, GUIBase&) override;
virtual bool undoable() const override;
private:
DTree::iterator this_cell;
bool was_running_, new_running_;
};
/// \ingroup clientserver
///
/// Update the list of referenced variables in this cell.
class ActionSetVariableList : public ActionBase {
public:
ActionSetVariableList(DataCell::id_t ref_id_, std::set);
virtual ~ActionSetVariableList() {};
virtual void execute(DocumentThread&, GUIBase&) override;
virtual void revert(DocumentThread&, GUIBase&) override;
virtual bool undoable() const override;
private:
DTree::iterator this_cell;
std::set new_variables_;
};
/// \ingroup clientserver
///
/// Remove a cell and all its child cells from the document.
class ActionRemoveCell : public ActionBase {
public:
ActionRemoveCell(DataCell::id_t ref_id_);
virtual ~ActionRemoveCell();
virtual void execute(DocumentThread&, GUIBase&) override;
virtual void revert(DocumentThread&, GUIBase&) override;
private:
// Keep track of the location where this cell (and its child
// cells) was in the notebook. We keep a reference to the
// parent cell and the index of the current cell as child of
// that parent.
DTree removed_tree;
DTree::iterator reference_parent_cell;
size_t reference_child_index;
};
/// \ingroup clientserver
///
/// Replace the contents of a cell. Not undo-able.
class ActionReplaceCell : public ActionBase {
public:
ActionReplaceCell(DataCell::id_t ref_id_);
virtual ~ActionReplaceCell();
virtual void execute(DocumentThread&, GUIBase&) override;
virtual void revert(DocumentThread&, GUIBase&) override;
virtual bool undoable() const override;
private:
};
/// \ingroup clientserver
///
/// Split a cell into two separate cells, at the point of the cursor.
class ActionSplitCell : public ActionBase {
public:
ActionSplitCell(DataCell::id_t ref_id);
virtual ~ActionSplitCell();
virtual void execute(DocumentThread&, GUIBase&) override;
virtual void revert(DocumentThread&, GUIBase&) override;
private:
DTree::iterator newref; // the newly created cell
};
/// \ingroup clientserver
///
/// Run a cell or run all cells.
class ActionRunCell : public ActionBase {
public:
// Run a particular cell.
ActionRunCell(DataCell::id_t ref_id);
// Run all cells.
ActionRunCell();
virtual ~ActionRunCell();
virtual void execute(DocumentThread&, GUIBase&) override;
virtual void revert(DocumentThread&, GUIBase&) override;
virtual bool undoable() const override;
private:
bool run_all_cells;
};
/// \ingroup clientserver
///
/// Open a notebook from a file, in the current window.
class ActionOpen : public ActionBase {
public:
ActionOpen(const std::string&);
virtual ~ActionOpen();
virtual void execute(DocumentThread&, GUIBase&) override;
virtual void revert(DocumentThread&, GUIBase&) override;
virtual bool undoable() const override;
private:
std::string notebook_name;
};
/// \ingroup clientserver
///
/// Add a text string (can be just a single character) at the point
/// of the cursor.
/// This action is assumed to be triggered from a user change to
/// the GUI cells, so will not update the GUI itself, only the
/// underlying DTree. However, the revert method will need to
/// update the GUI representation.
class ActionInsertText : public ActionBase {
public:
ActionInsertText(DataCell::id_t ref_id, int pos, const std::string&);
virtual ~ActionInsertText() {};
virtual void execute(DocumentThread&, GUIBase&) override;
virtual void revert(DocumentThread&, GUIBase&) override;
private:
DTree::iterator this_cell;
int insert_pos;
std::string text;
};
/// \ingroup clientserver
///
/// Complete text at a point in a GUI cell with one or more
/// alternative.
/// In contrast to ActionInsertText, this one is triggered from
/// the server-side, so will update the GUI both for execute
/// and revert.
class ActionCompleteText : public ActionBase {
public:
ActionCompleteText(DataCell::id_t ref_id, int pos, const std::string&, int alternative);
virtual ~ActionCompleteText() {};
virtual void execute(DocumentThread&, GUIBase&) override;
virtual void revert(DocumentThread&, GUIBase&) override;
int length() const;
int alternative() const;
private:
DTree::iterator this_cell;
int insert_pos;
std::string text;
int alternative_; // in case there is more than one completion alternative
};
/// \ingroup clientserver
///
/// Remove a text string starting at the indicated position, and
/// with the indicated length, from the indicated cell.
/// This action is assumed to be triggered from a user change to
/// the GUI cells, so will not update the GUI itself, only the
/// underlying DTree. However, the revert method will need to
/// update the GUI representation.
class ActionEraseText : public ActionBase {
public:
ActionEraseText(DataCell::id_t ref_id, int, int);
virtual ~ActionEraseText() {};
virtual void execute(DocumentThread&, GUIBase&) override;
virtual void revert(DocumentThread&, GUIBase&) override;
private:
DTree::iterator this_cell;
int from_pos, to_pos;
std::string removed_text;
};
}
//
// class ActionMergeCells
================================================
FILE: client_server/CMakeLists.txt
================================================
# cmake_minimum_required(VERSION ${CADABRA_CMAKE_VERSION})
project(Cadabra)
if(POLICY CMP0167)
cmake_policy(SET CMP0167 NEW)
endif()
#---------------------------------------------------------------------------
# Preamble.
#---------------------------------------------------------------------------
print_header("Configuring client-server")
set(INSTALL_LATEX_DIR "share/cadabra2")
#---------------------------------------------------------------------------
# Locate libraries.
#---------------------------------------------------------------------------
if(USE_GTK4)
find_package(GLIBMM4 REQUIRED)
else()
find_package(GLIBMM3 REQUIRED)
endif()
find_package(SQLITE3 REQUIRED)
# Don't set pthreads to required. Either we're on a platform where explict
# linking with -lpthread is the norm (e.g. Linux) and it'll be found, or we're
# on a platform that include pthreads by default (e.g. BSD, macOS) where this
# won't find anything, or we're on a road-much-less-traveled OS where the user
# can figure out what's wrong without a hard error here.
find_package(Threads)
# We need at least Boost 1.71.0 because we now use `beast` (for the
# websocket functionality) which was not stable before that version.
# Actually, it is probably not stable before 1.75.0, but if we go that
# high we cannot build on Ubuntu 20.04 anymore.
find_package(Boost 1.71.0 COMPONENTS program_options date_time filesystem REQUIRED)
# OpenSSL needs to be linked in explicitly (probably because the boost
# material referring to it is all in headers?). If you use a cmake module,
# you will run into issues on MSYS2, so do *not* be tempted to do that
# unless you test on MSYS2 first.
set(OpenSSL_USE_STATIC_LIBS OFF)
find_package(OpenSSL REQUIRED)
message(STATUS "OPENSSL_LIBRARIES: ${OPENSSL_LIBRARIES}")
message(STATUS "OPENSSL_SSL_LIBRARY: ${OPENSSL_SSL_LIBRARY}")
message(STATUS "OPENSSL_CRYPTO_LIBRARY: ${OPENSSL_CRYPTO_LIBRARY}")
message(STATUS "OPENSSL_INCLUDE_DIR: ${OPENSSL_INCLUDE_DIR}")
#---------------------------------------------------------------------------
# Enumerate input files.
#---------------------------------------------------------------------------
set(CADABRA_SERVER_SRC
cadabra-server.cc
Server.cc
Snoop.cc
websocket_client.cc
websocket_server.cc
${CADABRA_CORE_DIR}/InstallPrefix.cc
${CADABRA_CORE_DIR}/DataCell.cc
${CADABRA_CORE_DIR}/Exceptions.cc
${CADABRA_CORE_DIR}/CdbPython.cc
${CADABRA_CORE_DIR}/Stopwatch.cc
${CADABRA_CORE_DIR}/pythoncdb/py_helpers.cc
${CADABRA_LIBS_DIR}/whereami/whereami.c
${CADABRA_LIBS_DIR}/base64/base64.cc
)
set(CADABRA_CLIENT_SRC
ScriptThread.cc
ComputeThread.cc
DocumentThread.cc
Actions.cc
Snoop.cc
websocket_client.cc
websocket_server.cc
${CADABRA_CORE_DIR}/DataCell.cc
${CADABRA_CORE_DIR}/Exceptions.cc
${CADABRA_CORE_DIR}/InstallPrefix.cc
${CADABRA_CORE_DIR}/Stopwatch.cc
${CADABRA_LIBS_DIR}/whereami/whereami.c
)
set(JUPYTER_KERNEL_SRC
cadabra-jupyter-kernel.cc
cadabra-jupyter-kernel.hh
cadabra-jupyter-main.cc
Server.cc
${CADABRA_CORE_DIR}/InstallPrefix.cc
${CADABRA_CORE_DIR}/DataCell.cc
${CADABRA_CORE_DIR}/Exceptions.cc
${CADABRA_CORE_DIR}/CdbPython.cc
${CADABRA_CORE_DIR}/Stopwatch.cc
${CADABRA_LIBS_DIR}/whereami/whereami.c
${CADABRA_LIBS_DIR}/base64/base64.cc
)
# set(CONNECTION_FILE ${CMAKE_CURRENT_SOURCE_DIR}/connection.json)
#
# add_custom_command(TARGET echo_kernel POST_BUILD
# COMMAND ${CMAKE_COMMAND} -E
# copy "${CONNECTION_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/")
#
# add_custom_command(TARGET echo_kernel POST_BUILD
# COMMAND ${CMAKE_COMMAND} -E
# copy "${CMAKE_BINARY_DIR}/${XEUS_RUNTIME_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/")
#---------------------------------------------------------------------------
# Include directories and preprocessor directives.
#---------------------------------------------------------------------------
include_directories(
"."
"${CADABRA_CORE_DIR}"
"${CADABRA_LIBS_DIR}/pybind11/include"
# "${CADABRA_LIBS_DIR}/websocketpp"
"${CADABRA_LIBS_DIR}/internal/include"
"${CADABRA_LIBS_DIR}/whereami"
"${CADABRA_LIBS_DIR}/base64"
"${CADABRA_LIBS_DIR}/nlohmann"
${OPENSSL_INCLUDE_DIR}
${Boost_INCLUDE_DIRS}
${Python_INCLUDE_DIRS}
${SQLITE3_INCLUDE_DIR}
)
# add_definitions(
# -D_WEBSOCKETPP_CPP11_STL_
# -DBOOST_ASIO_HAS_STD_CHRONO
# -DBOOST_BIND_GLOBAL_PLACEHOLDERS
# )
#---------------------------------------------------------------------------
# Make targets.
#---------------------------------------------------------------------------
# add_executable(tst tst.cc)
# target_link_libraries(tst
# ${Boost_LIBRARIES}
# Threads::Threads
# )
# Server executable
if(WIN32)
message(STATUS "Using the win32 subsystem for cadabra-server")
add_executable(cadabra-server WIN32 ${CADABRA_SERVER_SRC})
else()
add_executable(cadabra-server ${CADABRA_SERVER_SRC})
endif()
# target_link_libraries(cadabra-server cadabra_server)
target_link_libraries(cadabra-server
pybind11::embed
${Boost_LIBRARIES}
${SQLITE3_LIBRARIES}
${Python_LIBRARIES}
${GLIBMM_LIBRARIES}
${OPENSSL_LIBRARIES}
Threads::Threads
pthread
)
if(WIN32)
target_link_libraries(cadabra-server ws2_32 mswsock bcrypt)
endif()
if(UNIX)
if(${CMAKE_SYSTEM_NAME} MATCHES ".*BSD|DragonFly")
target_link_libraries(cadabra-server util)
else()
target_link_libraries(cadabra-server dl util)
endif()
endif()
# Client library
add_library(cadabra_client STATIC ${CADABRA_CLIENT_SRC})
target_link_libraries(cadabra_client
${Boost_LIBRARIES}
${SQLITE3_LIBRARIES}
${Python_LIBRARIES}
${GLIBMM_LIBRARIES}
Threads::Threads
${OPENSSL_LIBRARIES}
)
if(WIN32)
target_link_libraries(cadabra_client ws2_32 bcrypt)
endif()
# cadabra2html
add_executable(cadabra2html
cadabra2html.cc
${CADABRA_LIBS_DIR}/base64/base64.cc
)
target_link_libraries(cadabra2html cadabra_client)
# cadabra2latex
add_executable(cadabra2latex
cadabra2latex.cc
${CADABRA_LIBS_DIR}/base64/base64.cc
)
target_link_libraries(cadabra2latex cadabra_client)
# Make sure sqlite3.dll is copied into the build directory on Windows.
if (WIN32)
add_custom_command(TARGET cadabra_client POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${SQLITE3_LIBRARIES}
${PROJECT_BINARY_DIR}/${CADABRA_BUILD_TYPE}/sqlite3.dll
)
endif()
#---------------------------------------------------------------------------
# Installation.
#---------------------------------------------------------------------------
# The preamble 'notebook.tex' should be kept in sync with the one in
# ../frontend/common/texengine; this one is meant for printing so
# slightly different, but cadabra-specific macros should be copied.
if (NOT INSTALL_TARGETS_ONLY)
install(FILES notebook.tex DESTINATION ${INSTALL_LATEX_DIR})
install(FILES notebook.html DESTINATION ${INSTALL_LATEX_DIR})
endif()
#set(INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries")
install(TARGETS cadabra-server DESTINATION ${CDB_BIN_PATH})
install(TARGETS cadabra2html cadabra2latex DESTINATION ${CDB_BIN_PATH})
# We don't need to install the libcadabra_client library as it is statically
# linked with our binaries anyway.
# install(
# TARGETS cadabra_client
# RUNTIME DESTINATION bin COMPONENT runtime
# LIBRARY ARCHIVE DESTINATION lib
# LIBRARY DESTINATION lib
# )
# install(TARGETS cadabra_server LIBRARY ARCHIVE DESTINATION lib LIBRARY DESTINATION lib)
if(ENABLE_JUPYTER)
install(TARGETS cadabra-jupyter-kernel DESTINATION ${CDB_BIN_PATH})
install(FILES kernel.json connection.json DESTINATION share/jupyter/kernels/cadabra)
endif()
if (MSVC AND NOT INSTALL_TARGETS_ONLY)
# GTK helpers
install(
FILES
"${_VCPKG_ROOT_DIR}/installed/x64-windows/tools/glib/gspawn-win64-helper.exe"
"${_VCPKG_ROOT_DIR}/installed/x64-windows/tools/glib/gspawn-win64-helper-console.exe"
DESTINATION
${CDB_BIN_PATH}
)
# Python (for some reason this is not copied in)
get_filename_component(CDB_PYTHON_BASE ${Python_LIBRARIES} NAME_WE)
get_filename_component(CDB_PYTHON_PATH ${Python_LIBRARIES} DIRECTORY)
message("--- Python directory: ${CDB_PYTHON_PATH}")
string(TOLOWER ${CDB_PYTHON_BASE} CDB_PYTHON_BASE)
message("--- Python library: ${CDB_PYTHON_BASE}.dll")
install(FILES "${CDB_PYTHON_PATH}/../${CDB_PYTHON_BASE}.dll"
DESTINATION ${CDB_BIN_PATH}
)
endif()
# manual pages
if(NOT MSVC)
install(
FILES
../man/man1/cadabra-server.1
DESTINATION
share/man/man1
)
endif()
================================================
FILE: client_server/ComputeThread.cc
================================================
#include
#include
#include
#include "ComputeThread.hh"
#include "DocumentThread.hh"
#include "GUIBase.hh"
#include "Actions.hh"
#include "InstallPrefix.hh"
#include "popen2.hh"
#include
#include
#include "internal/unistd.h"
#include "CdbPython.hh"
using namespace cadabra;
ComputeThread::ComputeThread(int server_port, std::string token, std::string ip_address)
: gui(0), docthread(0), connection_is_open(false), restarting_kernel(false), server_pid(0),
server_stdout(0), server_stderr(0), forced_server_port(server_port), forced_server_token(token),
forced_server_ip_address(ip_address)
{
// The ComputeThread constructor (but _not_ the run() member!) is
// always run on the gui thread, so we can grab the gui thread id
// here.
gui_thread_id=std::this_thread::get_id();
}
ComputeThread::~ComputeThread()
{
if(server_stdout!=0) {
close(server_stdout);
// close(server_stderr);
Glib::spawn_close_pid(server_pid);
server_pid=0;
server_stdout=0;
server_stderr=0;
}
}
void ComputeThread::set_master(GUIBase *b, DocumentThread *d)
{
gui=b;
docthread=d;
}
void ComputeThread::init()
{
// Setup the WebSockets client.
}
void ComputeThread::try_connect()
{
wsclient.set_connect_handler(std::bind(&ComputeThread::on_open, this));
wsclient.set_fail_handler(std::bind(&ComputeThread::on_fail, this, std::placeholders::_1));
wsclient.set_close_handler(std::bind(&ComputeThread::on_close, this));
wsclient.set_message_handler(std::bind(&ComputeThread::on_message, this, std::placeholders::_1));
std::ostringstream uristr;
uristr << "ws://" << (forced_server_ip_address.empty() ? "127.0.0.1" : forced_server_ip_address) << ":" << port;
wsclient.connect(uristr.str());
// std::cerr << "cadabra-client: connect done" << std::endl;
}
void ComputeThread::run()
{
// This does *not* run on the GUI thread.
init();
try_spawn_server();
try_connect();
// Enter run loop, which will never terminate anymore. The on_fail and on_close
// handlers will re-try to establish connections when they go bad.
wsclient.run();
}
void ComputeThread::terminate()
{
wsclient.stop();
// If we have started the server ourselves, stop it now so we do
// not leave mess behind.
// http://riccomini.name/posts/linux/2012-09-25-kill-subprocesses-linux-bash/
if(server_pid!=0) {
std::cerr << "cadabra-client: killing server" << std::endl;
if(server_stdout!=0) {
close(server_stdout);
// close(server_stderr);
Glib::spawn_close_pid(server_pid);
server_pid=0;
server_stdout=0;
server_stderr=0;
}
// kill(server_pid, SIGKILL);
// if(server_stdout)
// pclose2(server_stdout, server_pid);
}
}
bool ComputeThread::kernel_is_connected() const
{
return connection_is_open;
}
void ComputeThread::all_cells_nonrunning()
{
for(auto it: running_cells) {
std::shared_ptr rs_action =
std::make_shared(it.first, false);
docthread->queue_action(rs_action);
}
if(gui) {
gui->process_data();
gui->on_kernel_runstatus(false);
}
running_cells.clear();
}
void ComputeThread::on_fail(const boost::beast::error_code& ec)
{
if(!restarting_kernel) {
std::cerr << "cadabra-client: connection to server on port " << port << " failed, " << ec.message() << std::endl;
}
connection_is_open=false;
all_cells_nonrunning();
if(gui && server_pid!=0) {
// When a kernel restart is in progress, server_pid will be zero
// and this block never runs.
close(server_stdout);
// close(server_stderr);
// std::cerr << "closing connetion to terminated server" << std::endl;
Glib::spawn_close_pid(server_pid);
// kill(server_pid, SIGKILL);
server_pid=0;
server_stdout=0;
server_stderr=0;
gui->on_network_error();
}
try_spawn_server();
try_connect();
}
using SlotSpawnChildSetup = sigc::slot;
void ComputeThread::try_spawn_server()
{
// Startup the server. First generate a UUID, pass this to the
// starting server, then use this UUID to get access to the server
// port.
// std::cerr << "cadabra-client: spawning server" << std::endl;
if(forced_server_port!=0) {
port=forced_server_port;
authentication_token=forced_server_token;
return;
}
std::vector argv, envp;
#if defined(_WIN32) || defined(_WIN64)
argv.push_back("cadabra-server.exe");
#else
const char *appdir = getenv("APPDIR");
if(appdir) {
// std::cerr << "This is an AppImage, APPDIR = " << appdir << std::endl;
argv.push_back(std::string(appdir)+"/usr/bin/cadabra-server");
}
else {
// std::cerr << "Not an AppImage." << std::endl;
argv.push_back("cadabra-server");
}
#endif
Glib::Pid pid;
std::string wd("");
// See https://bugs.launchpad.net/inkscape/+bug/1662531 for things related to
// the 'envp' argument in the call below.
try {
#ifdef _WIN32
Glib::SpawnFlags flags = Glib::SPAWN_DO_NOT_REAP_CHILD | Glib::SPAWN_SEARCH_PATH | Glib::SpawnFlags::SPAWN_STDERR_TO_DEV_NULL;
#else
#if GLIBMM_MAJOR_VERSION > 2 || (GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION >= 68)
Glib::SpawnFlags flags = Glib::SpawnFlags::DEFAULT | Glib::SpawnFlags::SEARCH_PATH,
#else
Glib::SpawnFlags flags = Glib::SPAWN_DO_NOT_REAP_CHILD | Glib::SPAWN_SEARCH_PATH;
#endif
#endif
Glib::spawn_async_with_pipes(wd, argv, /* envp, WITH envp, Fedora 27 fails to start python properly */
flags,
sigc::slot(),
&pid,
0,
&server_stdout,
0); // We need to see stderr on the console
// &server_stderr);
//#if GLIBMM_MAJOR_VERSION > 2 || (GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION >= 68)
// Glib::SpawnFlags::DEFAULT | Glib::SpawnFlags::SEARCH_PATH,
// sigc::slot(),
//#else
// flags,
// sigc::slot([](){ FreeConsole(); }),
//#endif
// &pid,
// 0,
// &server_stdout,
// 0); // We need to see stderr on the console
// // &server_stderr);
char buffer[100];
FILE *f = fdopen(server_stdout, "r");
if(fscanf(f, "%99s", buffer)!=1) {
throw std::logic_error("Failed to read port from server.");
}
port = atoi(buffer);
if(fscanf(f, "%99s", buffer)!=1) {
throw std::logic_error("Failed to read authentication token from server.");
}
authentication_token=std::string(buffer);
// std::cerr << "auth token: " << authentication_token << std::endl;
}
catch(Glib::SpawnError& err) {
std::cerr << "Failed to start server " << argv[0] << ": " << err.what() << std::endl;
// FIXME: cannot just fall through, the server is not up!
}
}
void ComputeThread::on_open()
{
connection_is_open=true;
restarting_kernel=false;
if(gui) {
gui->on_connect();
gui->on_kernel_runstatus(false);
}
// // now it is safe to use the connection
// std::string msg;
//
//// if(stopit) {
//// msg =
//// "{ \"header\": { \"uuid\": \"none\", \"msg_type\": \"execute_interrupt\" },"
//// " \"content\": { \"code\": \"print(42)\n\"} "
//// "}";
//// }
//// else {
// msg =
// "{ \"header\": { \"uuid\": \"none\", \"msg_type\": \"execute_request\" },"
// " \"content\": { \"code\": \"import time\nprint(42)\ntime.sleep(10)\n\"} "
// "}";
//// }
//
//// c->send(hdl, "import time\nfor i in range(0,10):\n print('this is python talking '+str(i))\nex=Ex('A_{m n}')\nprint(str(ex))", websocketpp::frame::opcode::text);
// c->send(hdl, msg, websocketpp::frame::opcode::text);
}
void ComputeThread::on_close()
{
// std::cerr << "cadabra-client: connection closed" << std::endl;
connection_is_open=false;
all_cells_nonrunning();
if(gui) {
if(restarting_kernel) {
gui->on_disconnect("restarting kernel");
gui->on_kernel_runstatus(true);
}
else {
gui->on_disconnect("not connected");
}
}
sleep(1); // do not cause a torrent...
try_connect();
}
void ComputeThread::cell_finished_running(DataCell::id_t id)
{
if(id.id==0) {
// This was code without a cell representation (run in
// response to e.g. a slider update). Ignore.
}
else {
auto it=running_cells.find(id);
if(it==running_cells.end()) {
throw std::logic_error("Cannot find cell with id = "+std::to_string(id.id));
}
if(it->second==1) {
// Mark this cell as no longer running.
std::shared_ptr rs_action =
std::make_shared(id, false);
docthread->queue_action(rs_action);
running_cells.erase(it);
}
else
it->second -= 1;
}
}
void ComputeThread::on_message(const std::string& msg)
{
// Parse the JSON message.
nlohmann::json root;
try {
root=nlohmann::json::parse(msg);
}
catch(nlohmann::json::exception& e) {
std::cerr << "cadabra-client: cannot parse message." << std::endl;
return;
}
if(getenv("CADABRA_SHOW_RECEIVED")) {
std::cerr << "RECV: " << root.dump(3) << std::endl;
}
if(root.count("header")==0) {
std::cerr << "cadabra-client: received message without 'header'." << std::endl;
return;
}
if(root.count("content")==0) {
std::cerr << "cadabra-client: received message without 'content'." << std::endl;
return;
}
const nlohmann::json& header = root["header"];
const nlohmann::json& content = root["content"];
const std::string msg_type = root.value("msg_type", "");
DataCell::id_t parent_id;
parent_id.id = header.value("parent_id", uint64_t(0));
if(header.value("parent_origin", "")=="client")
parent_id.created_by_client=true;
else
parent_id.created_by_client=false;
DataCell::id_t cell_id;
cell_id.id = header["cell_id"].get();
if(header.value("cell_origin", "")=="client")
cell_id.created_by_client=true;
else
cell_id.created_by_client=false;
// std::cerr << "received cell with id " << cell_id.id << std::endl;
// Determine if this refers to a special cell in the interactive console.
if (interactive_cells.find(parent_id.id) != interactive_cells.end()) {
interactive_cells.insert(cell_id.id);
docthread->on_interactive_output(root);
}
else if (interactive_cells.find(cell_id.id) != interactive_cells.end()) {
docthread->on_interactive_output(root);
}
else if (msg_type.find("csl_") == 0) {
root["header"]["from_server"] = true;
docthread->on_interactive_output(root);
}
else if (msg_type == "progress_update") {
std::string msg = content.value("msg", "Idle");
int n = content.value("n", 0);
int total = content.value("total", 0);
// FIXME: do something with 'pulse':
// int pulse = content.value("pulse", false);
docthread->set_progress(msg, n, total);
}
else if(msg_type=="completed") {
// std::cerr << "received completion of " << content["original"] << " -> " << content["completed"] << std::endl;
// Finally, the action to add the output cell.
std::string toadd=content["completed"].get();
if(toadd.size()>0) {
toadd=toadd.substr(content["original"].get().size());
int pos=content["position"].get();
int alternative=content["alternative"].get();
std::shared_ptr action =
std::make_shared(cell_id, pos, toadd, alternative);
docthread->queue_action(action);
}
}
else {
try {
bool finished = header["last_in_sequence"].get();
if (finished) {
if(parent_id.id!=0) {
// If this cell references variables, store them in the DataCell.
if(content.count("variables")>0) {
std::set vars;
for(const auto& var: content["variables"])
vars.insert(var);
std::shared_ptr action =
std::make_shared(parent_id, vars);
docthread->queue_action(action);
}
}
cell_finished_running(parent_id);
}
if (content.count("output")>0 && content["output"].get().size() > 0) {
if (msg_type == "output") {
std::string output = "\\begin{verbatim}" + content["output"].get() + "\\end{verbatim}";
// Stick an AddCell action onto the stack. We instruct the
// action to add this result output cell as a child of the
// corresponding input cell.
DataCell result(cell_id, DataCell::CellType::output, output);
// Finally, the action to add the output cell.
std::shared_ptr action =
std::make_shared(result, parent_id, ActionAddCell::Position::child);
docthread->queue_action(action);
}
else if (msg_type == "verbatim") {
std::string output = "\\begin{verbatim}" + content["output"].get() + "\\end{verbatim}";
// Stick an AddCell action onto the stack. We instruct the
// action to add this result output cell as a child of the
// corresponding input cell.
DataCell result(cell_id, DataCell::CellType::verbatim, output);
// Finally, the action to add the output cell.
std::shared_ptr action =
std::make_shared(result, parent_id, ActionAddCell::Position::child);
docthread->queue_action(action);
}
else if (msg_type == "latex_view") {
// std::cerr << "received latex cell " << content["output"].asString() << std::endl;
DataCell result(cell_id, DataCell::CellType::latex_view, content["output"].get());
std::shared_ptr action =
std::make_shared(result, parent_id, ActionAddCell::Position::child);
docthread->queue_action(action);
}
else if (msg_type == "input_form") {
DataCell result(cell_id, DataCell::CellType::input_form, content["output"].get());
std::shared_ptr action =
std::make_shared(result, parent_id, ActionAddCell::Position::child);
docthread->queue_action(action);
}
else if (msg_type == "error" || msg_type=="fault") {
std::string error = "\\begin{verbatim}" + content["output"].get()
+ "\\end{verbatim}";
//if (msg_type == "fault") {
// error = "Kernel fault\\begin{small}" + error + "\\end{small}";
// }
// Stick an AddCell action onto the stack. We instruct the
// action to add this result output cell as a child of the
// corresponding input cell.
DataCell result(cell_id, DataCell::CellType::error, error);
// Finally, the action.
std::shared_ptr action =
std::make_shared(result, parent_id, ActionAddCell::Position::child);
docthread->queue_action(action);
// Position the cursor in the cell that generated the error. All other cells on
// the execute queue have been cancelled by the server.
std::shared_ptr actionpos =
std::make_shared(parent_id, ActionPositionCursor::Position::in);
docthread->queue_action(actionpos);
// Action has stopped, so mark all cells as non-running.
all_cells_nonrunning();
}
else if (msg_type == "image_png") {
DataCell result(cell_id, DataCell::CellType::image_png, content["output"].get());
std::shared_ptr action =
std::make_shared(result, parent_id, ActionAddCell::Position::child);
docthread->queue_action(action);
}
else if (msg_type == "image_svg") {
DataCell result(cell_id, DataCell::CellType::image_svg, content["output"].get());
std::shared_ptr action =
std::make_shared(result, parent_id, ActionAddCell::Position::child);
docthread->queue_action(action);
}
else if (msg_type == "slider") {
DataCell result(cell_id, DataCell::CellType::slider, content["output"].get());
std::shared_ptr action =
std::make_shared(result, parent_id, ActionAddCell::Position::child);
docthread->queue_action(action);
}
else {
std::cerr << "cadabra-client: received cell we did not expect: "
<< msg_type << ": " << content << std::endl;
}
}
}
catch (std::logic_error& ex) {
// WARNING: if the server sends
std::cerr << "cadabra-client: trouble processing server response: " << ex.what() << std::endl;
}
}
// Update kernel busy indicator depending on number of running cells.
if(number_of_cells_executing()>0)
gui->on_kernel_runstatus(true);
else
gui->on_kernel_runstatus(false);
gui->process_data();
}
void ComputeThread::execute_interactive(uint64_t id, const std::string& code)
{
assert(gui_thread_id == std::this_thread::get_id());
if (!connection_is_open)
return;
if (code.substr(0, 7) == "reset()")
return restart_kernel();
nlohmann::json req, header, content;
header["msg_type"] = "execute_request";
header["cell_id"] = id;
header["interactive"] = true;
content["code"] = code.c_str();
req["auth_token"] = authentication_token;
req["header"] = header;
req["content"] = content;
std::ostringstream oss;
oss << req << std::endl;
if(getenv("CADABRA_SHOW_SENT")) {
std::cerr << "SENT: " << req.dump(3) << std::endl;
}
wsclient.send(oss.str());
interactive_cells.insert(id);
}
void ComputeThread::execute_cell(DTree::iterator it, std::string no_assign, std::vector output_cell_ids)
{
// This absolutely has to be run on the main GUI thread.
assert(gui_thread_id==std::this_thread::get_id());
if(connection_is_open==false)
return;
const DataCell& dc=(*it);
// std::cout << "cadabra-client: ComputeThread going to execute " << dc.textbuf << std::endl;
if((it->textbuf).substr(0,7)=="reset()") {
restart_kernel();
std::shared_ptr action =
std::make_shared(it->id(), ActionPositionCursor::Position::next);
docthread->queue_action(action);
return;
}
// Position the cursor in the next cell so this one will not
// accidentally get executed twice. This runs synchronously!
std::shared_ptr actionpos =
std::make_shared(it->id(), ActionPositionCursor::Position::next);
docthread->queue_action(actionpos);
gui->process_data();
// For a code cell, construct a server request message and then
// send the cell to the server.
if(it->cell_type==DataCell::CellType::python) {
auto rit=running_cells.find(dc.id());
if(rit==running_cells.end())
running_cells[dc.id()]=1;
else
rit->second += 1;
// Schedule an action to update the running status of this cell.
std::shared_ptr rs_action =
std::make_shared(it->id(), true);
docthread->queue_action(rs_action);
nlohmann::json req, header, content;
header["uuid"]="none";
header["cell_id"]=dc.id().id;
if(dc.id().created_by_client)
header["cell_origin"]="client";
else
header["cell_origin"]="server";
header["msg_type"]="execute_request";
header["output_cell_ids"]=output_cell_ids;
req["auth_token"]=authentication_token;
req["header"]=header;
content["remove_variable_assignments"]=no_assign;
content["code"]=dc.textbuf;
req["content"]=content;
gui->on_kernel_runstatus(true);
std::ostringstream str;
str << req << std::endl;
if(getenv("CADABRA_SHOW_SENT")) {
std::cerr << "SENT: " << req.dump(3) << std::endl;
}
wsclient.send(str.str());
// NOTE: we can get a return message in on_message at any point after this,
// it will come in on a different thread!
}
// For a LaTeX cell, immediately request a new latex output cell to be displayed.
if(it->cell_type==DataCell::CellType::latex) {
// Stick an AddCell action onto the stack. We instruct the
// action to add this result output cell as a child of the
// corresponding input cell.
DataCell result(DataCell::CellType::latex_view, it->textbuf);
std::shared_ptr action =
std::make_shared(result, it->id(), ActionAddCell::Position::child);
docthread->queue_action(action);
}
}
void ComputeThread::update_variable_on_server(std::string variable, double value)
{
nlohmann::json req, header, content;
header["uuid"]="none";
header["cell_id"]=0;
header["cell_origin"]="client";
header["msg_type"]="execute_request";
req["auth_token"]=authentication_token;
req["header"]=header;
content["code"]=variable + "=" + std::to_string(value);
req["content"]=content;
std::ostringstream str;
str << req << std::endl;
if(getenv("CADABRA_SHOW_SENT")) {
std::cerr << "SENT: " << req.dump(3) << std::endl;
}
wsclient.send(str.str());
}
int ComputeThread::number_of_cells_executing() const
{
return running_cells.size();
}
void ComputeThread::stop()
{
if(connection_is_open==false)
return;
// std::cerr << "stopping existing kernel" << std::endl;
nlohmann::json req, header, content;
header["uuid"]="none";
header["msg_type"]="execute_interrupt";
req["auth_token"]=authentication_token;
req["header"]=header;
std::ostringstream str;
str << req << std::endl;
// std::cerr << str.str() << std::endl;
server_pid=0;
if(getenv("CADABRA_SHOW_SENT")) {
std::cerr << "SENT: " << req.dump(3) << std::endl;
}
wsclient.send(str.str());
// Do not yet mark cells non-running, otherwise we are unable to
// process any error messages. Do this once the stop comes through.
// all_cells_nonrunning();
}
void ComputeThread::restart_kernel()
{
if(connection_is_open==false)
return;
restarting_kernel=true;
// Restarting the kernel means all previously running blocks have stopped running.
// Inform the GUI about this.
// FIXME: set all running flags to false
gui->on_kernel_runstatus(false);
// std::cerr << "cadabra-client: restarting kernel" << std::endl;
nlohmann::json req, header, content;
header["uuid"]="none";
header["msg_type"]="exit";
header["from_server"] = true;
req["auth_token"]=authentication_token;
req["header"]=header;
std::ostringstream str;
str << req << std::endl;
// std::cerr << str.str() << std::endl;
if(getenv("CADABRA_SHOW_SENT")) {
std::cerr << "SENT: " << req.dump(3) << std::endl;
}
wsclient.send(str.str());
docthread->on_interactive_output(req);
}
bool ComputeThread::complete(DTree::iterator it, int pos, int alternative)
{
if(connection_is_open==false)
return false;
const DataCell& dc=(*it);
nlohmann::json req, header, content;
header["uuid"]="none";
header["cell_id"]=dc.id().id;
if(dc.id().created_by_client)
header["cell_origin"]="client";
else
header["cell_origin"]="server";
header["msg_type"]="complete";
req["auth_token"]=authentication_token;
req["header"]=header;
std::string todo = it->textbuf.substr(0,pos);
// if(todo.size()>0 && todo[todo.size()-1]=='\n')
// todo=todo.substr(0, todo.size()-1);
// std::cerr << "to complete full: " << todo << std::endl;
size_t lst=todo.find_last_of("\n(){}[]:\t ");
if(lst!=std::string::npos)
todo=todo.substr(lst+1);
// std::cerr << "to complete strip: " << todo << std::endl;
if(todo.size()==0)
return false;
req["string"]=todo;
req["position"]=pos;
req["alternative"]=alternative;
std::ostringstream str;
str << req << std::endl;
// std::cerr << str.str() << std::endl;
server_pid=0;
if(getenv("CADABRA_SHOW_SENT")) {
std::cerr << "SENT: " << req.dump(3) << std::endl;
}
wsclient.send(str.str());
return true;
}
================================================
FILE: client_server/ComputeThread.hh
================================================
#pragma once
#include
#include
#include
#include
#include "websocket_client.hh"
#include "DataCell.hh"
namespace cadabra {
class GUIBase;
class DocumentThread;
/// \ingroup clientserver
///
/// Base class which talks to the server and sends Action objects back to the
/// DocumentThread.
///
/// ComputeThread is the base class which takes care of doing actual
/// computations with the cells in a document. It handles talking to
/// the server backend. It knows how to pass cells to the server and
/// ask them to be executed. Results are reported back to the GUI by
/// putting ActionBase objects onto its todo stack. ComputeThread never
/// directly modifies the document tree.
class ComputeThread {
public:
/// If the ComputeThread is constructed with a null pointer to the
/// gui, there will be no gui updates, just DTree updates.
ComputeThread(int server_port=0, std::string token="", std::string ip_address="127.0.0.1");
ComputeThread(const ComputeThread& )=delete; // You cannot copy this object
~ComputeThread();
/// Determine the objects that this compute thread should be
/// talking to.
void set_master(GUIBase *, DocumentThread *);
/// Main entry point, which will connect to the server and
/// then start an event loop to handle communication with the
/// server. Only terminates when the connection drops, so run
/// your GUI on a different thread.
void run();
/// In order to execute code on the server, call the
/// following from the GUI thread. This method returns as
/// soon as the request has been put on the network queue. If
/// no communication with the server is necessary, this
/// returns immediately. The ComputeThread will report the
/// result of the computation/processing by adding actions to
/// the DocumentThread owned pending_actions stack, by
/// calling queue_action. It will never modify the cell
/// directly, and will also never modify any other cells in
/// the document tree.
void execute_cell(DTree::iterator, std::string no_assign="",
std::vector output_cell_ids=std::vector() );
void execute_interactive(uint64_t id, const std::string& code);
/// Update a variable in the kernel. This does essentially the
/// same thing as execute_cell, but will not refer to any
/// cell as it creates the code itself.
void update_variable_on_server(std::string variable, double value);
/// Stop the current cell execution on the server and remove
/// all other cells from the run queue as well.
void stop();
/// Restart the kernel.
void restart_kernel();
/// Request completion of a string. Returns `false` if the current
/// cell cannot be completed, in which case the TAB which led to
/// the request should be interpreted literally and used for spacing.
/// The `alternative` argument is the serial number of the requested
/// completion, in case there is more than one possible completion.
bool complete(DTree::iterator, int pos, int alternative);
// Determine if there are still cells running on the server.
// FIXME: this does not guarantee thread-safety but at the moment
// is only used for updating status bars etc.
// FIXME: can be moved to DocumentThread.
int number_of_cells_executing(void) const;
/// Terminate the compute thread, in preparation for shutting
/// down the client altogether.
void terminate();
/// Return the status of the connection to the kernel.
bool kernel_is_connected() const;
private:
GUIBase *gui;
DocumentThread *docthread;
// For debugging purposes, we keep record of the gui thread id,
// so that we can flag when code runs on the wrong thread.
// Gets initialised in the ComputeThread constructor.
std::thread::id gui_thread_id;
// Keeping track of cells which are running on the server, in
// a form which allows us to look them up quickly based only
// on the id (which is all that the server knows about). A
// cell can be queued multiple times for running; this is the
// integer in the map.
std::map running_cells;
// WebSocket++ things.
websocket_client wsclient;
bool connection_is_open, restarting_kernel;
void init();
void try_connect();
void try_spawn_server();
void on_open();
void on_fail(const boost::beast::error_code&);
void on_close();
void on_message(const std::string& msg);
void cell_finished_running(DataCell::id_t);
/// Set all cells to be non-running (e.g. after a kernel failure) and
/// report the status of each cell to the GUI.
void all_cells_nonrunning();
std::set interactive_cells;
// Self-started server
Glib::Pid server_pid;
int server_stdout, server_stderr;
unsigned short port;
std::string authentication_token;
int forced_server_port;
std::string forced_server_token;
std::string forced_server_ip_address;
};
}
================================================
FILE: client_server/DocumentThread.cc
================================================
#include "Actions.hh"
#include "DocumentThread.hh"
#include "GUIBase.hh"
#include "ComputeThread.hh"
#include "CdbPython.hh"
#include
#include
#include
#include
#include
//#include
#include
#include
#ifndef EMSCRIPTEN
#include
#include "Snoop.hh"
#endif
#include "Config.hh"
using namespace cadabra;
DocumentThread::DocumentThread(GUIBase* g)
: gui(g)
, compute(0)
, current_cell(doc.end())
, follow_mode(false)
, follow_cell(doc.end())
, follow_last_cell(doc.end())
, disable_stacks(false)
{
// Setup logging.
std::string version=std::string(CADABRA_VERSION_SEM);
#ifndef EMSCRIPTEN
snoop::log.init("Cadabra", version, "log.cadabra.science");
snoop::log.set_sync_immediately(true);
#endif
// snoop::log(snoop::warn) << "Starting" << snoop::flush;
}
void DocumentThread::on_interactive_output(const nlohmann::json& )
{
}
void DocumentThread::set_progress(const std::string& msg, int cur_step, int total_steps)
{
}
void DocumentThread::set_compute_thread(ComputeThread *cl)
{
compute = cl;
}
void DocumentThread::new_document()
{
// Setup a single-cell document. This operation itself cannot be undone,
// so we do it directly on the doc, not using Actions.
DataCell top(DataCell::CellType::document);
DTree::iterator doc_it = doc.set_head(top);
gui->add_cell(doc, doc_it, false);
// One Python input cell in the empty document.
DataCell one(DataCell::CellType::python, "");
DTree::iterator one_it = doc.append_child(doc_it, one);
gui->add_cell(doc, one_it, false);
// Put a 'position cursor' action on the stack to be executed as
// soon as the GUI is up.
std::shared_ptr actionpos =
std::make_shared(one_it->id(), ActionPositionCursor::Position::in);
queue_action(actionpos);
}
void DocumentThread::load_from_string(const std::string& json)
{
std::lock_guard guard(stack_mutex);
pending_actions=std::queue >();
doc.clear();
JSON_deserialise(json, doc);
gui->remove_all_cells();
build_visual_representation();
}
void DocumentThread::undo()
{
stack_mutex.lock();
if(undo_stack.size()==0) {
//std::cerr << "no entries left on the stack" << std::endl;
stack_mutex.unlock();
return;
}
disable_stacks=true;
auto ua = undo_stack.top();
//std::cerr << "Undo action " << typeid(*ua).name() << std::endl;
redo_stack.push(ua);
undo_stack.pop();
// std::cerr << "DocumentThread::undo: undo_stack.size() == " << undo_stack.size() << std::endl;
ua->revert(*this, *gui);
disable_stacks=false;
stack_mutex.unlock();
}
void DocumentThread::redo()
{
stack_mutex.lock();
if(redo_stack.size()==0) {
//std::cerr << "no entries left on the stack" << std::endl;
stack_mutex.unlock();
return;
}
disable_stacks=true;
auto ua = redo_stack.top();
//std::cerr << "Undo action " << typeid(*ua).name() << std::endl;
undo_stack.push(ua);
redo_stack.pop();
ua->execute(*this, *gui);
disable_stacks=false;
stack_mutex.unlock();
}
void DocumentThread::build_visual_representation()
{
// Because the add_cell method figures out by itself where to generate the VisualCell,
// we only have feed all cells in turn.
DTree::iterator doc_it=doc.begin();
while(doc_it!=doc.end()) {
// std::cout << "ADDING:" << doc_it->textbuf << std::endl;
gui->add_cell(doc, doc_it, false);
++doc_it;
}
}
//const DTree& DocumentThread::dtree()
// {
// return doc;
// }
template
struct ci_equal {
bool operator()(charT ch1, charT ch2) {
return std::toupper(ch1) == std::toupper(ch2);
}
};
template
int ci_find_substr( const T& str1, const T& str2, int start_pos )
{
auto start=str1.begin();
start+=start_pos;
typename T::const_iterator it = std::search( start, str1.end(),
str2.begin(), str2.end(), ci_equal() );
if ( it != str1.end() ) return it - str1.begin();
else return -1;
}
std::pair DocumentThread::find_string(DTree::iterator start_it, size_t start_pos, const std::string& f, bool case_ins) const
{
// std::cerr << "finding from pos " << start_pos << ", " << &(*start_it) << ": " << start_it->textbuf.substr(0,30) << std::endl;
DTree::iterator doc_it=start_it;
while(doc_it!=doc.end()) {
// std::cout << doc_it->textbuf << std::endl;
// FIXME: re-enable searching in output cells.
if(doc_it->hidden==false && (doc_it->cell_type==DataCell::CellType::python || doc_it->cell_type==DataCell::CellType::latex)) {
size_t pos;
if(case_ins)
pos = ci_find_substr(doc_it->textbuf, f, start_pos);
else
pos = doc_it->textbuf.find(f, start_pos);
if(pos!=std::string::npos)
return std::make_pair(doc_it, pos);
}
start_pos=0; // after one fail, start next cell at zero
++doc_it;
}
return std::make_pair(doc.end(), std::string::npos);
}
void DocumentThread::queue_action(std::shared_ptr ab)
{
std::lock_guard guard(stack_mutex);
pending_actions.push(ab);
}
void DocumentThread::run_all_cells()
{
follow_mode=true;
DTree::sibling_iterator sib=doc.begin(doc.begin());
while(sib!=doc.end(doc.begin())) {
if(sib->cell_type==DataCell::CellType::python) {
run_cell(DTree::iterator(sib), false);
follow_last_cell=DTree::iterator(sib);
}
++sib;
}
}
void DocumentThread::run_cell(DTree::iterator it, bool shift_pressed)
{
// First ensure that this cell is not already running, otherwise all hell
// will break loose when we try to double-remove the existing output cell etc.
if(it->running)
return;
// Ensure this cell is not empty either.
if(it->textbuf.size()==0)
return;
// Remove child nodes, if any.
// FIXME: Does it make more sense to do this only after the
// execution result comes back from the server?
DTree::sibling_iterator sib=doc.begin(it);
gui->dim_output_cells(it);
while(sib!=doc.end(it)) {
// std::cout << "cadabra-client: scheduling output cell for removal: " << sib->id().id << std::endl;
std::shared_ptr action = std::make_shared(sib->id());
queue_action(action);
++sib;
}
// Since the user has initiated this cell execution, we can
// turn on cell follow mode.
follow_cell=it;
follow_mode=true;
// Execute the cell.
// std::cerr << "Executing cell " << it->id().id << std::endl;
// If this is a LaTeX input cell, and auto-close is turned on, close
// the input cell. Make sure to also feed that into the document
// itself!
if(it->cell_type==DataCell::CellType::latex)
if(prefs.auto_close_latex)
gui->hide_visual_cells(it);
// Execute the cell. Make sure this comes after the hiding logic above.
compute->execute_cell(it);
}
void DocumentThread::run_cells_referencing_variable(std::string variable, double value)
{
// First update the variable itself.
compute->update_variable_on_server(variable, value);
// Re-run all cells referencing this variable.
follow_mode=false;
DTree::iterator it = doc.begin();
while(it!=doc.end()) {
if(it->cell_type==DataCell::CellType::python) {
if(it->variables_referenced.count(variable)==1) {
if(it->textbuf.find("slider(")==std::string::npos) {
// We have found a cell which depends on the variable.
// Collect the cell_id's of the current output cells,
// so that we can re-use these.
std::vector output_cell_ids;
DTree::iterator sib = doc.begin(it);
while(sib != doc.end(it)) {
output_cell_ids.push_back(sib->id().id);
++sib;
}
// Now execute.
compute->execute_cell(it, variable, output_cell_ids);
}
}
}
++it;
}
}
void DocumentThread::process_action_queue()
{
// This routine *absolutely* has to be run on the main GUI thread. Anything
// else is a bug.
if(main_thread_id != std::this_thread::get_id())
std::cerr << "INTERNAL ERROR: DocumentThread::process_action_queue not running on main thread."
<< std::endl;
stack_mutex.lock();
while(pending_actions.size()>0) {
// std::cerr << "pending_actions.size() == " << pending_actions.size() << std::endl;
std::shared_ptr ab = pending_actions.front();
// Unlock the action queue while we are processing this particular action,
// so that other actions can be added which we run.
stack_mutex.unlock();
// std::cerr << "Executing action " << typeid(*ab).name() << " for " << ab->ref_id.id << std::endl;
// Execute the action; this will run synchronously, so after
// this returns the doc and visual representation have both been
// updated.
try {
ab->execute(*this, *gui);
}
catch (const std::exception& err) {
on_unhandled_error(err);
}
// Lock the queue to remove the action just executed, and
// add it to the undo stack.
stack_mutex.lock();
if(ab->undoable())
undo_stack.push(ab);
if(ab->callback != nullptr)
ab->callback();
if(pending_actions.size()>0) // some actions clear the queue
pending_actions.pop();
}
stack_mutex.unlock();
}
bool DocumentThread::on_unhandled_error(const std::exception& err)
{
return true;
}
DocumentThread::Prefs::Prefs(bool use_defaults)
{
#ifndef EMSCRIPTEN
config_path=std::string(Glib::get_user_config_dir()) + "/cadabra2.conf";
try {
if (!use_defaults) {
std::ifstream f(config_path);
if (f) {
try {
f >> data;
}
catch(nlohmann::json::exception& ex) {
std::cerr << "Config file " << config_path << " is not JSON; ignoring." << std::endl;
data = nlohmann::json::object();
}
}
else {
data = nlohmann::json::object();
// Backwards compatibility, check to see if cadabra.conf exists
// and if so take the is_registered variable from there
std::ifstream old_f(std::string(Glib::get_user_config_dir()) + "/cadabra.conf");
if (old_f) {
std::string line;
while (old_f.good()) {
std::getline(old_f, line);
if (line.find("registered=true") != std::string::npos) {
data["is_registered"] = true;
break;
}
}
}
}
}
}
catch(std::exception& ex) {
data = nlohmann::json::object();
}
font_step = data.value("font_step", 0);
highlight = data.value("highlight", false);
is_registered = data.value("is_registered", false);
is_anonymous = data.value("is_anonymous", false);
git_path = data.value("git_path", "");
python_path = data.value("python_path", "");
move_into_new_cell = data.value("move_into_new_cell", false);
tab_completion = data.value("tab_completion", true);
microtex = data.value("microtex", true);
auto_close_latex = data.value("auto_close_latex", true);
// Force microtex when this is an AppImage.
const char *appdir = getenv("APPDIR");
if(appdir)
microtex=true;
// Force microtex when we are on Windows.
#if(_WIN32)
microtex = true;
#endif
if(git_path=="")
git_path="/usr/bin/git";
// Get the colours for syntax highlighting.
if(data.count("colours")==0)
data["colours"]={ {"python", nlohmann::json::object() }, {"latex", nlohmann::json::object() } };
const auto& python_colours = data["colours"]["python"];
colours["python"]["keyword"] = python_colours.value("keyword", "RoyalBlue");
colours["python"]["operator"] = python_colours.value("operator", "SlateGray");
colours["python"]["brace"] = python_colours.value("brace", "SlateGray");
colours["python"]["string"] = python_colours.value("string", "ForestGreen");
colours["python"]["comment"] = python_colours.value("comment", "Silver");
colours["python"]["object"] = python_colours.value("object", "DarkGray");
colours["python"]["number"] = python_colours.value("number", "Sienna");
colours["python"]["maths"] = python_colours.value("maths", "Olive");
colours["python"]["function"] = python_colours.value("function", "FireBrick");
colours["python"]["decorator"] = python_colours.value("decorator", "DarkViolet");
colours["python"]["class"] = python_colours.value("class", "MediumOrchid");
const auto& latex_colours = data["colours"]["latex"];
colours["latex"]["command"] = latex_colours.value("command", "rgb(52,101,164)");
colours["latex"]["parameter"] = latex_colours.value("brace", "rgb(245,121,0)");
colours["latex"]["comment"] = latex_colours.value("comment", "Silver");
colours["latex"]["maths"] = latex_colours.value("maths", "Sienna");
#endif
}
void DocumentThread::Prefs::save()
{
std::ofstream f(config_path);
if (f) {
data["font_step"] = font_step;
data["highlight"] = highlight;
data["is_registered"] = is_registered;
data["is_anonymous"] = is_anonymous;
data["python_path"] = python_path;
data["move_into_new_cell"] = move_into_new_cell;
data["tab_completion"] = tab_completion;
data["microtex"] = microtex;
data["auto_close_latex"] = auto_close_latex;
for (const auto& lang : colours) {
for (const auto& kw : lang.second)
data["colours"][lang.first][kw.first] = kw.second;
}
data["git_path"] = git_path;
f << data << '\n';
}
else
std::cerr << "Warning: could not write to config file\n";
}
void DocumentThread::set_user_details(const std::string& name, const std::string& email, const std::string& affiliation)
{
#ifndef EMSCRIPTEN
snoop::log("name") << name << snoop::flush;
snoop::log("email") << email << snoop::flush;
snoop::log("affiliation") << affiliation << snoop::flush;
#endif
}
bool DocumentThread::help_type_and_topic(const std::string& before, const std::string& after,
help_t& help_type, std::string& help_topic) const
{
help_t objtype=help_t::algorithm;
if(! (before.size()==0 && after.size()==0) ) {
// We provide help for properties, algorithms and reserved node
// names. Properties are delimited to the left by '::' and to
// the right by anything non-alnum. Algorithms are delimited to
// the left by non-alnum except '_' and to the right by '('. Reserved node
// names are TeX symbols, starting with '\'.
//
// So scan the 'before' string for a left-delimiter and the 'after' string
// for a right-delimiter.
int lpos=before.size()-1;
while(lpos>=0) {
if(before[lpos]==':' && lpos>0 && before[lpos-1]==':') {
objtype=help_t::property;
break;
}
if(before[lpos]=='\\') {
objtype=help_t::latex;
break;
}
if(isalnum(before[lpos])==0 && before[lpos]!='_') {
objtype=help_t::algorithm;
break;
}
--lpos;
}
if(objtype==help_t::none) return false;
++lpos;
size_t rpos=0;
while(rpos
#include
#include
#include