{"id":17321745,"url":"https://github.com/liuliu/swift-mujoco","last_synced_at":"2025-10-06T04:43:07.720Z","repository":{"id":37375163,"uuid":"473330422","full_name":"liuliu/swift-mujoco","owner":"liuliu","description":"Swift Binding for MuJoCo: https://mujoco.org/","archived":false,"fork":false,"pushed_at":"2022-12-31T20:25:58.000Z","size":2098,"stargazers_count":18,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-04-24T20:43:43.731Z","etag":null,"topics":["reinforcement-learning","simulation"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/liuliu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-03-23T19:29:00.000Z","updated_at":"2024-02-26T16:17:06.000Z","dependencies_parsed_at":"2022-07-08T07:39:23.487Z","dependency_job_id":null,"html_url":"https://github.com/liuliu/swift-mujoco","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuliu%2Fswift-mujoco","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuliu%2Fswift-mujoco/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuliu%2Fswift-mujoco/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liuliu%2Fswift-mujoco/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/liuliu","download_url":"https://codeload.github.com/liuliu/swift-mujoco/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248912070,"owners_count":21182208,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["reinforcement-learning","simulation"],"created_at":"2024-10-15T13:39:23.822Z","updated_at":"2025-10-06T04:43:02.675Z","avatar_url":"https://github.com/liuliu.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MuJoCo for Swift\n\n[![macos-spm](https://github.com/liuliu/swift-mujoco/actions/workflows/macos-spm.yaml/badge.svg?branch=main)](https://github.com/liuliu/swift-mujoco/actions/workflows/macos-spm.yaml?query=branch%3Amain)\n[![macos-bazel](https://github.com/liuliu/swift-mujoco/actions/workflows/macos-bazel.yaml/badge.svg?branch=main)](https://github.com/liuliu/swift-mujoco/actions/workflows/macos-bazel.yaml?query=branch%3Amain)\n[![ubuntu-spm](https://github.com/liuliu/swift-mujoco/actions/workflows/ubuntu-spm.yaml/badge.svg?branch=main)](https://github.com/liuliu/swift-mujoco/actions/workflows/ubuntu-spm.yaml?query=branch%3Amain)\n[![ubuntu-bazel](https://github.com/liuliu/swift-mujoco/actions/workflows/ubuntu-bazel.yaml/badge.svg?branch=main)](https://github.com/liuliu/swift-mujoco/actions/workflows/ubuntu-bazel.yaml?query=branch%3Amain)\n[![documentation](https://github.com/liuliu/swift-mujoco/actions/workflows/documentation.yaml/badge.svg?branch=main)](https://github.com/liuliu/swift-mujoco/actions/workflows/documentation.yaml?query=branch%3Amain)\n\nThis is a Swift binding for [MuJoCo](https://mujoco.org) physics simulation library.\n\n[MuJoCo](https://mujoco.org) is a very accurate CPU-based physics simulation library. Since its acquisition by [DeepMind](https://github.com/deepmind/mujoco), MuJoCo has been more readily available broadly.\n\nMuJoCo is at the heart of physics simulation in the realm of [deep reinforcement learning](https://spinningup.openai.com/en/latest/algorithms/ppo.html). In [OpenAI Gym](https://www.gymlibrary.ml/environments/mujoco/), there are many diverse environments simulated with MuJoCo.\n\nUp until now, I only dipped a bit into DRL with https://github.com/liuliu/s4nnc/tree/main/examples. OpenAI Gym is OK for these simple mechanisms and I don't mind to wait a few minutes more. But most recently, I grown interests in more serious use of DRL / PPO under Sim2Real banner. For these, it is popular to either use [Isaac Gym](https://developer.nvidia.com/isaac-gym) or exploiting multi-core for environment runs. For later, doing it with `multiprocess` package of Python is awkward but the preferred way.\n\nIf we can run MuJoCo from Swift, we can avoid the trip into Python entirely and simply use `DispatchQueue.concurrentPerform` for environment runs.\n\n## Installation\n\nMuJoCo for Swift can be installed through either [Swift Package Manager](https://www.swift.org/package-manager/) or Bazel.\n\n### Install with Swift Package Manager\n\nYou can add MuJoCo for Swift in your project from here:\n\n```swift\n.package(name: \"MuJoCo\", url: \"https://github.com/liuliu/swift-mujoco.git\", from: \"2.3.0\")\n```\n\n### Install with Bazel\n\nYou can simply add MuJoCo for Swift as a dependency in `WORKSPACE`:\n\n```python\ngit_repository(\n    name = \"swift-mujoco\",\n    commit = \"3dfc5a10f3d09f96432efc7804b0aedfa866d0e3\",\n    remote = \"https://github.com/liuliu/swift-mujoco.git\",\n    shallow_since = \"1667838646 -0500\"\n)\n\nload(\"@swift-mujoco//:deps.bzl\", \"swift_mujoco_deps\")\n\nswift_mujoco_deps()\n```\n\nMuJoCo for Swift should work on macOS, Linux and iOS. The `GLContext` is only available on macOS and Linux.\n\n## Automatic Interface Generation\n\n### Struct\n\nA mixed strategy was used for MuJoCo interface generation. MuJoCo's API heavily leans towards exposed public C structs. MuJoCo for Swift has to expose these C structs. Wrappers for these C structs are manually created with following principles:\n\n * If possible, simply `typealias` to its C struct;\n * If not, refer a `public struct` with C struct interior, keeping the memory layout exactly the same (can better expose certain properties, more on that later);\n * If there are associated heap data, typically, with `mj_deleteXXX` methods. These are `public struct` with a `final class Storage` interior to track lifetime.\n\nWhich out of the three implemented was tracked in `Sources/codegen/functionExtension.swift`. We later generated `Sources/MjObject+Extensions.swift` to encode above strategies into Swift protocol. All `MjStruct` conforms to this protocol which provides associated type of the underlying C struct, as well as the memory access method `withCTypeUnsafeMutablePointer(to:)`.\n\nMuJoCo's C headers are parsed and used to generate properties on structs and functions on structs. These are `XX+Extensions.swift` and `XX+Functions.swift` files. All accessors are generated for public C structs from MuJoCo.\n\nThey can be reproduced with:\n\n```sh\nbazel run Sources:codegen -- ~/workspace/swift-mujoco/Sources/ ~/workspace/mujoco/include/mujoco/*.h\n```\n\nExtra care was taken to make sure comments are properly parsed and added to these extensions. \u003chttps://liuliu.github.io/swift-mujoco\u003e is the documentation generated based on these comments.\n\n`MjArray\u003cT\u003e` is the array type that exposes static or dynamic arrays within a C struct. It retains the underlying storage to make sure the access is safe. It also conforms to set of protocols such that for `inout` parameters and other array parameters, you can either pass vanilla array or `MjArray\u003cT\u003e` (or `MjTuple`, through .tuple()) and expect it just works.\n\n### Enum\n\n`enum` are parsed and generated in `Mjt.swift` file. Because there is no type annotation, we do our best to infer which int type are enums from comment. `MjtEnableBit`, `MjtDisableBit`, `MjtCatBit` and `MjtPertBit` are special handled to be `OptionSet`.\n\n### Functions\n\nC functions are not scoped to a particular object. We have to infer these. First, we try to infer which group of APIs it belongs to with the prefix: `mj_` matches `mjXX` structs, `mjv_` matches `mjvXX` structs. We prefer the first parameter, otherwise the last parameter (as it is often used as write to). If no suitable object to be found, it is a free function.\n\nThe interface generation process also treats `const` annotation seriously. Without `const`, these methods are annotated with `mutating`. If the parameter is a pointer without `const`, it is annotated with `inout`.\n\nNot all functions are automatically generated, some of these are manually implemented because:\n\n * The C API can be better refactored to Swift API. For example, `mj_loadXML` can be better translated with `throws` for errors;\n * Some parameters can be nil, but during automatic interface generation, we assumed all parameters to be non-nil.\n\n### MjUI\n\nWhenever possible, we lean towards automatic interface generation. That's why we kept certain APIs in a way that look weird. `MjUI` has many quirks that at odds with this philosophy. In particular, `MjuiDef` automatically associated the UI control with the underlying storage `pdata`. Because `pdata` is a pointer, it has no regards to the underlying storage lifetime. A set of property wrappers: `MjuiDefState`, `MjuiDefStateMap` are introduced to solve this issue. Although users are still responsible to make sure the underlying storage lifetime longer than `MjUI` itself, you don't need to deal with raw `pdata`. A `MjuiDefObjectMapper` is introduced to facilitate direct mapping between an `MjStruct` and a `MjuiDef`. You need to pay extra attention because there is no guarantee on underlying `MjStruct` lifetime with `MjuiDefObjectMapper`. A recommended way is to group them both under a `final class` to make sure their lifetime is in sync.\n\n### Constants\n\nOnly constants related to `MjUI` are ported over so far.\n\n### Callbacks\n\nCallbacks can be found under `Mjcb`. These are manually ported callbacks but does support Swift closures (i.e. captures).\n\n### Reflection\n\n`CustomReflectable` protocol conformance is implemented for MjStruct. These should contain all the fields in C structs with very few exceptions (`buffer` and `stack` in `MjData`, `buffer` in `MjModel` and `userdata` in `MjUI`).\n\n### String\n\nMuJoCo leans heavily on static allocated strings. To make interaction easier, these are exposed as ordinary Swift strings. The back-and-forth copying can be expensive. Because static allocated strings have hard limit, there is a silent truncation if such limit reached.\n\n### GLContext\n\nA `GLContext` object is introduced to delegate GLFW interactions. Functionalities from `uitools.cc` in `./sample/` of MuJoCo were added to make interactions with `MjUI` easier. This object is a bit overreaching as it provides access to clipboard, timing and drag \u0026 drop functions.\n\n## Examples\n\nBoth `Examples/simulate` and `Examples/ant` should provide good starting points to learn about the APIs. To run:\n\n```sh\nbazel run Examples:ant\nbazel run --compilation_mode=opt Examples:simulate -- ~/workspace/swift-mujoco/Examples/assets/ant.xml\n```\n\nVisit documentation at: \u003chttps://liuliu.github.io/swift-mujoco\u003e. These should be kept up-to-date with `main` branch.\n\n## Appendix\n\n### List of Supported MuJoCo C APIs\n\n```c\nmj_defaultVFS\nmj_addFileVFS\nmj_makeEmptyFileVFS\nmj_findFileVFS\nmj_deleteFileVFS\nmj_deleteVFS\nmj_loadXML\nmj_defaultLROpt\nmj_defaultSolRefImp\nmj_defaultOption\nmj_defaultVisual\nmj_loadModel\nmj_deleteModel\nmj_makeData\nmj_deleteData\nmjv_defaultCamera\nmjv_defaultPerturb\nmjv_defaultOption\nmjv_defaultFigure\nmjv_defaultScene\nmjv_updateScene\nmjv_freeScene\nmjr_defaultContext\nmjr_freeContext\nmjui_themeSpacing\nmjui_themeColor\nmjui_add\nmjui_addToSection\nmjui_event\nmjr_readPixels\nmjr_drawPixels\nmj_setLengthRange\nmjr_findRect\nmj_copyModel\nmj_saveModel\nmj_sizeModel\nmj_copyData\nmj_stackAlloc\nmj_saveLastXML\nmj_freeLastXML\nmj_printSchema\nmj_step\nmj_step1\nmj_step2\nmj_forward\nmj_inverse\nmj_forwardSkip\nmj_inverseSkip\nmj_resetData\nmj_resetDataDebug\nmj_resetDataKeyframe\nmj_setConst\nmj_printFormattedModel\nmj_printModel\nmj_printFormattedData\nmj_printData\nmju_printMat\nmju_printMatSparse\nmj_fwdPosition\nmj_fwdVelocity\nmj_fwdActuation\nmj_fwdAcceleration\nmj_fwdConstraint\nmj_Euler\nmj_RungeKutta\nmj_invPosition\nmj_invVelocity\nmj_invConstraint\nmj_compareFwdInv\nmj_sensorPos\nmj_sensorVel\nmj_sensorAcc\nmj_energyPos\nmj_energyVel\nmj_checkPos\nmj_checkVel\nmj_checkAcc\nmj_kinematics\nmj_comPos\nmj_camlight\nmj_tendon\nmj_transmission\nmj_crb\nmj_factorM\nmj_solveM\nmj_solveM2\nmj_comVel\nmj_passive\nmj_subtreeVel\nmj_rne\nmj_rnePostConstraint\nmj_collision\nmj_makeConstraint\nmj_projectConstraint\nmj_referenceConstraint\nmj_constraintUpdate\nmj_addContact\nmj_isPyramidal\nmj_isSparse\nmj_isDual\nmj_mulJacVec\nmj_mulJacTVec\nmj_jac\nmj_jacBody\nmj_jacBodyCom\nmj_jacGeom\nmj_jacSite\nmj_jacPointAxis\nmj_name2id\nmj_id2name\nmj_fullM\nmj_mulM\nmj_mulM2\nmj_addM\nmj_applyFT\nmj_objectVelocity\nmj_objectAcceleration\nmj_contactForce\nmj_differentiatePos\nmj_integratePos\nmj_normalizeQuat\nmj_local2Global\nmj_getTotalmass\nmj_setTotalmass\nmj_version\nmj_versionString\nmj_ray\nmj_rayHfield\nmj_rayMesh\nmjd_transitionFD\nmju_rayGeom\nmju_raySkin\nmjv_room2model\nmjv_model2room\nmjv_cameraInModel\nmjv_cameraInRoom\nmjv_frustumHeight\nmjv_alignToCamera\nmjv_moveCamera\nmjv_movePerturb\nmjv_moveModel\nmjv_initPerturb\nmjv_applyPerturbPose\nmjv_applyPerturbForce\nmjv_averageCamera\nmjv_select\nmjv_initGeom\nmjv_makeConnector\nmjv_makeScene\nmjv_addGeoms\nmjv_makeLights\nmjv_updateCamera\nmjv_updateSkin\nmjr_makeContext\nmjr_changeFont\nmjr_addAux\nmjr_uploadTexture\nmjr_uploadMesh\nmjr_uploadHField\nmjr_restoreBuffer\nmjr_setBuffer\nmjr_blitBuffer\nmjr_setAux\nmjr_blitAux\nmjr_text\nmjr_overlay\nmjr_maxViewport\nmjr_rectangle\nmjr_label\nmjr_figure\nmjr_render\nmjui_resize\nmjui_update\nmjui_render\nmju_error\nmju_error_i\nmju_error_s\nmju_warning\nmju_warning_i\nmju_warning_s\nmju_clearHandlers\nmj_warning\nmju_writeLog\nmju_type2Str\nmju_warningText\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliuliu%2Fswift-mujoco","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliuliu%2Fswift-mujoco","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliuliu%2Fswift-mujoco/lists"}