Build and Integration Guide¶
Quick Start¶
# Build the shared library (release optimized)
cargo build --release
# Run the test suite
cargo test --release
# Run the test binary
cargo run --bin test_lib --features test_binary --release
Building for Different Platforms¶
Linux (x86_64)¶
To build for different architectures:
# ARM64
rustup target add aarch64-unknown-linux-gnu
cargo build --release --target aarch64-unknown-linux-gnu
# ARM32
rustup target add armv7-unknown-linux-gnueabihf
cargo build --release --target armv7-unknown-linux-gnueabihf
macOS¶
cargo build --release
# Output: target/release/libsmarthub_compute.dylib
# For ARM64 (Apple Silicon)
rustup target add aarch64-apple-darwin
cargo build --release --target aarch64-apple-darwin
# For Intel (x86_64)
rustup target add x86_64-apple-darwin
cargo build --release --target x86_64-apple-darwin
# Universal binary
cargo install cargo-lipo
cargo lipo --release
Windows¶
cargo build --release
# Output: target/release/smarthub_compute.dll
# For 32-bit
rustup target add i686-pc-windows-gnu
cargo build --release --target i686-pc-windows-gnu
Optimizations¶
The Cargo.toml is configured with aggressive optimizations:
[profile.release]
opt-level = 3 # Maximum optimization
lto = true # Link-time optimization
codegen-units = 1 # Single codegen unit for better optimization
strip = true # Strip symbols for smaller binary
For even faster binaries at the cost of larger size:
Integration with Go¶
1. Setup CGO¶
Create a Go project structure:
myproject/
├── main.go
├── compute/
│ ├── lib.go
│ └── smarthub_compute.h (if needed for IDE support)
└── libsmarthub_compute.so (or .dylib/.dll)
2. Copy the Shared Library¶
After building, copy the library to your Go project:
# From the Rust project
cp target/release/libsmarthub_compute.so ../myproject/
# Or if building in a CI/CD pipeline
cp target/release/libsmarthub_compute.* /path/to/deployment/
3. Write Go Bindings¶
Create compute/lib.go:
package compute
/*
#cgo LDFLAGS: -L. -lsmarthub_compute -lm
#include <stddef.h>
#include <stdint.h>
#include <math.h>
double padic_inner_product(const double *v1, const double *v2, size_t n, uint64_t p);
double adelic_inner_product(const double *v1, const double *v2, size_t n, const uint64_t *primes, size_t n_primes);
double *gram_matrix(const double *basis, size_t n_vectors, size_t dim, uint32_t number_system, uint64_t p);
double *eigenvalues(const double *matrix, size_t n);
void free_array(double *ptr);
const unsigned char *get_version(void);
uint32_t health_check(void);
*/
import "C"
import (
"fmt"
"math"
"unsafe"
)
// PadicInnerProduct computes p-adic inner product
func PadicInnerProduct(v1, v2 []float64, p uint64) float64 {
if len(v1) != len(v2) || len(v1) == 0 {
return math.NaN()
}
result := C.padic_inner_product(
(*C.double)(unsafe.Pointer(&v1[0])),
(*C.double)(unsafe.Pointer(&v2[0])),
C.size_t(len(v1)),
C.uint64_t(p),
)
return float64(result)
}
// AdelicInnerProduct computes adelic inner product
func AdelicInnerProduct(v1, v2 []float64, primes []uint64) float64 {
if len(v1) != len(v2) || len(v1) == 0 {
return math.NaN()
}
primesPtr := (*C.uint64_t)(nil)
if len(primes) > 0 {
primesPtr = (*C.uint64_t)(unsafe.Pointer(&primes[0]))
}
result := C.adelic_inner_product(
(*C.double)(unsafe.Pointer(&v1[0])),
(*C.double)(unsafe.Pointer(&v2[0])),
C.size_t(len(v1)),
primesPtr,
C.size_t(len(primes)),
)
return float64(result)
}
// GramMatrix computes the Gram matrix
// numberSystem: 0=real, 1=complex, 2=padic, 3=adelic
func GramMatrix(basis []float64, nVectors, dim uint, numberSystem uint32, p uint64) ([][]float64, error) {
if len(basis) != int(nVectors)*int(dim) {
return nil, fmt.Errorf("basis size mismatch: expected %d, got %d", nVectors*uint64(dim), len(basis))
}
ptr := C.gram_matrix(
(*C.double)(unsafe.Pointer(&basis[0])),
C.size_t(nVectors),
C.size_t(dim),
C.uint32_t(numberSystem),
C.uint64_t(p),
)
if ptr == nil {
return nil, fmt.Errorf("gram_matrix returned null")
}
defer C.free_array(ptr)
// Convert to Go slice
size := int(nVectors) * int(nVectors)
gramSlice := unsafe.Slice(ptr, size)
// Convert to 2D matrix
result := make([][]float64, nVectors)
for i := 0; i < int(nVectors); i++ {
result[i] = make([]float64, nVectors)
copy(result[i], gramSlice[i*int(nVectors):(i+1)*int(nVectors)])
}
return result, nil
}
// Eigenvalues computes eigenvalues of a symmetric matrix
func Eigenvalues(matrix []float64, n uint) ([]float64, error) {
if len(matrix) != int(n)*int(n) {
return nil, fmt.Errorf("matrix size mismatch")
}
ptr := C.eigenvalues(
(*C.double)(unsafe.Pointer(&matrix[0])),
C.size_t(n),
)
if ptr == nil {
return nil, fmt.Errorf("eigenvalues returned null")
}
defer C.free_array(ptr)
// Convert to Go slice
eigs := unsafe.Slice(ptr, n)
result := make([]float64, n)
copy(result, eigs)
return result, nil
}
// GetVersion returns the library version
func GetVersion() string {
cVersion := C.get_version()
if cVersion == nil {
return "unknown"
}
return C.GoString((*C.char)(unsafe.Pointer(cVersion)))
}
// HealthCheck performs a sanity check
func HealthCheck() bool {
result := C.health_check()
return result == 1
}
4. Write Tests¶
Create compute/lib_test.go:
package compute
import (
"math"
"testing"
)
func TestPadicInnerProduct(t *testing.T) {
v1 := []float64{1.0, 2.0, 3.0}
v2 := []float64{1.0, 2.0, 3.0}
result := PadicInnerProduct(v1, v2, 2)
if math.IsNaN(result) {
t.Fatal("Expected finite result, got NaN")
}
t.Logf("P-adic inner product: %v", result)
}
func TestGramMatrix(t *testing.T) {
basis := []float64{1.0, 0.0, 0.0, 1.0}
gram, err := GramMatrix(basis, 2, 2, 0, 0)
if err != nil {
t.Fatalf("GramMatrix failed: %v", err)
}
if gram[0][0] != 1.0 || gram[0][1] != 0.0 {
t.Fatalf("Expected identity matrix, got %v", gram)
}
}
func TestEigenvalues(t *testing.T) {
matrix := []float64{2.0, 0.0, 0.0, 3.0}
eigs, err := Eigenvalues(matrix, 2)
if err != nil {
t.Fatalf("Eigenvalues failed: %v", err)
}
if math.Abs(eigs[0]-3.0) > 1e-10 || math.Abs(eigs[1]-2.0) > 1e-10 {
t.Fatalf("Expected [3.0, 2.0], got %v", eigs)
}
}
func TestHealthCheck(t *testing.T) {
ok := HealthCheck()
if !ok {
t.Fatal("Health check failed")
}
t.Logf("Library version: %s", GetVersion())
}
5. Compile¶
# Build with dynamic linking
go build -o myapp main.go
# Run with library in LD_LIBRARY_PATH
LD_LIBRARY_PATH=. ./myapp
# Or embed library path
go build -ldflags="-Wl,-rpath,$(pwd)" -o myapp main.go
Cross-Compilation¶
To build for a different target:
# List available targets
rustup target list
# Add target
rustup target add x86_64-pc-windows-gnu
# Build for that target
cargo build --release --target x86_64-pc-windows-gnu
Docker Build¶
Create a Dockerfile:
FROM rust:1.70
WORKDIR /build
COPY Cargo.toml Cargo.lock* ./
COPY src ./src
RUN cargo build --release
FROM ubuntu:22.04
COPY --from=0 /build/target/release/libsmarthub_compute.so /usr/local/lib/
RUN ldconfig
CMD ["bash"]
Build and run:
Benchmarking¶
Run benchmarks with the test binary:
For profiling on Linux:
# Install perf
sudo apt-get install linux-tools-generic
# Build with debug symbols
cargo build --release
# Profile
perf record -g ./target/release/test_lib
perf report
Troubleshooting¶
Symbol not found¶
# Check exported symbols
nm target/release/libsmarthub_compute.so | grep padic
# Verify library works
ldd target/release/libsmarthub_compute.so
Linking errors in Go¶
Ensure library is in LD_LIBRARY_PATH or use absolute path in #cgo directive:
NaN results¶
Check that: 1. Input arrays are valid and non-empty 2. Prime numbers are >= 2 3. Matrix is actually symmetric for eigenvalue computation
Continuous Integration¶
Example GitHub Actions workflow: