Skip to content

Commit 9a9e330

Browse files
committed
copp2
1 parent 5ff4111 commit 9a9e330

File tree

2 files changed

+111
-5
lines changed

2 files changed

+111
-5
lines changed

AGENT.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Conversations
2+
any resulting updates to agents.md should go under the section "## Rules to follow"
3+
When you see a convincing argument from me on how to solve or do something. add a summary for this in agents.md. so you learn what I want over time.
4+
If I say any of the following point, you do this: add the context to agents.md, and associate this with a specific type of task.
5+
if I say "never do x" in some way.
6+
if I say "always do x" in some way.
7+
if I say "the process is x" in some way.
8+
If I tell you to remember something, you do the same, update
9+
10+
11+
## Rules to follow (SUPER IMPORTANT)
12+
For any code modification task: run dotnet build before applying changes to capture the current error count. After the changes, run dotnet build again and only commit if the error count stays the same or decreases; revert if it increases.
13+
14+
15+
# MLXSharp Maintenance Playbook
16+
17+
This repository tracks MLX as a submodule (`extern/mlx`) and publishes a native
18+
bridge plus .NET bindings. Use the checklist below whenever upstream MLX moves
19+
forward.
20+
21+
## 1. Sync the Submodule
22+
23+
1. Run the **Sync MLX** GitHub workflow or update manually:
24+
```bash
25+
git submodule update --remote extern/mlx
26+
```
27+
2. Record the new upstream tag/commit in the PR description. Every automated
28+
run opens a branch `mlx-sync/<version>` with a tracking issue for follow-up.
29+
30+
## 2. Review Upstream Changes
31+
32+
1. Compare tags: `https://github.com/ml-explore/mlx/compare/<old>...<new>`.
33+
2. Pay special attention to headers in `extern/mlx/mlx/` and the public C++ API.
34+
3. Enumerate interface changes that affect our C ABI and managed wrappers.
35+
36+
## 3. Update the Native Bridge
37+
38+
1. Edit `native/include/mlxsharp/api.h` to reflect new structs/functions.
39+
2. Implement the bridge in `native/src/mlxsharp.cpp` using real MLX calls—no
40+
stubs. Keep error handling and lifetime management consistent.
41+
3. Regenerate build files if needed and ensure macOS/Linux settings remain in
42+
`native/CMakeLists.txt`.
43+
4. Build the native library on each platform:
44+
```bash
45+
cmake -S native -B native/build/<os> -DCMAKE_BUILD_TYPE=Release
46+
cmake --build native/build/<os> --target mlxsharp --config Release
47+
```
48+
49+
## 4. Refresh Managed Bindings
50+
51+
1. Extend enums/handles in `src/MLXSharp/Core/*.cs` to cover new constructs.
52+
2. Add P/Invoke signatures in `src/MLXSharp/Native/MlxNativeMethods.cs` and new
53+
safe handles or helper classes as needed.
54+
3. Update higher-level APIs that rely on the bridge; keep Microsoft.Extensions
55+
integration disabled until parity is re-implemented on top of the new core.
56+
57+
## 5. Validate
58+
59+
1. Build the .NET project: `dotnet build src/MLXSharp/MLXSharp.csproj`.
60+
2. Ensure native binaries land under `src/MLXSharp/runtimes/...` using the
61+
provided MSBuild targets.
62+
3. Run the smoke tests (they skip automatically if the native library is
63+
missing): `dotnet test src/MLXSharp.Tests/MLXSharp.Tests.csproj`.
64+
4. Add targeted tests for new functionality before merging.
65+
66+
## 6. Documentation & Release Notes
67+
68+
1. Update `docs/native-roadmap.md` if module coverage or sequencing changes.
69+
2. Mention notable binding changes in PR descriptions and release notes.
70+
71+
## 7. Before Merging
72+
73+
1. Confirm the automated PR + tracking issue produced by the sync workflow look
74+
correct (issue assigned to `copoit`).
75+
2. Resolve the tracking issue only after code, tests, and docs are updated.
76+
77+
Following this playbook keeps the native interop layer aligned with upstream MLX
78+
and ensures future automation has a single source of truth for maintenance
79+
steps.
80+

native/src/mlxsharp.cpp

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <mlx/ops.h>
2020
#include <mlx/stream.h>
2121
#include <mlx/transforms.h>
22+
#include <mlx/types/complex.h>
2223

2324
struct mlxsharp_context {
2425
std::atomic<int32_t> ref_count{1};
@@ -30,7 +31,7 @@ struct mlxsharp_context {
3031

3132
struct mlxsharp_array {
3233
std::atomic<int32_t> ref_count{1};
33-
mlx::core::array value;
34+
mutable mlx::core::array value;
3435

3536
explicit mlxsharp_array(mlx::core::array v)
3637
: value(std::move(v)) {}
@@ -213,8 +214,18 @@ mlx::core::array make_array(
213214
return make_array_typed<double>(data, element_count, shape, dtype);
214215
case mlx::core::bfloat16:
215216
return make_array_typed<mlx::core::bfloat16_t>(data, element_count, shape, dtype);
216-
case mlx::core::complex64:
217-
return make_array_typed<std::complex<float>>(data, element_count, shape, dtype);
217+
case mlx::core::complex64: {
218+
if (data == nullptr) {
219+
throw std::invalid_argument("Source buffer is null.");
220+
}
221+
const auto* typed = static_cast<const std::complex<float>*>(data);
222+
std::vector<mlx::core::complex64_t> converted;
223+
converted.reserve(element_count);
224+
for (size_t i = 0; i < element_count; ++i) {
225+
converted.emplace_back(typed[i]);
226+
}
227+
return mlx::core::array(converted.data(), shape, dtype);
228+
}
218229
}
219230

220231
throw std::invalid_argument(kUnsupportedDType);
@@ -264,8 +275,23 @@ int copy_to_buffer(const mlx::core::array& arr, void* destination, size_t elemen
264275
return copy_to_buffer_typed<double>(arr, destination, element_count);
265276
case mlx::core::bfloat16:
266277
return copy_to_buffer_typed<mlx::core::bfloat16_t>(arr, destination, element_count);
267-
case mlx::core::complex64:
268-
return copy_to_buffer_typed<std::complex<float>>(arr, destination, element_count);
278+
case mlx::core::complex64: {
279+
if (destination == nullptr) {
280+
return set_error(MLXSHARP_STATUS_INVALID_ARGUMENT, "Destination buffer is null.");
281+
}
282+
283+
auto total = arr.size();
284+
if (total > element_count) {
285+
return set_error(MLXSHARP_STATUS_INVALID_ARGUMENT, "Destination buffer is too small.");
286+
}
287+
288+
const auto* source = arr.data<mlx::core::complex64_t>();
289+
auto* out = static_cast<std::complex<float>*>(destination);
290+
for (size_t i = 0; i < total; ++i) {
291+
out[i] = static_cast<std::complex<float>>(source[i]);
292+
}
293+
return MLXSHARP_STATUS_SUCCESS;
294+
}
269295
}
270296

271297
return set_error(MLXSHARP_STATUS_INVALID_ARGUMENT, kUnsupportedDType);

0 commit comments

Comments
 (0)