{"id":17202671,"url":"https://github.com/deepcharles/csc-l0","last_synced_at":"2026-02-18T17:31:10.949Z","repository":{"id":154901086,"uuid":"626120972","full_name":"deepcharles/csc-l0","owner":"deepcharles","description":null,"archived":false,"fork":false,"pushed_at":"2025-02-25T08:31:33.000Z","size":15,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-16T15:10:03.922Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/deepcharles.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-04-10T20:57:27.000Z","updated_at":"2025-02-25T08:31:36.000Z","dependencies_parsed_at":"2025-04-11T13:04:15.272Z","dependency_job_id":"444d3143-5c93-4cac-8c27-e89a77df568f","html_url":"https://github.com/deepcharles/csc-l0","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/deepcharles/csc-l0","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deepcharles%2Fcsc-l0","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deepcharles%2Fcsc-l0/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deepcharles%2Fcsc-l0/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deepcharles%2Fcsc-l0/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/deepcharles","download_url":"https://codeload.github.com/deepcharles/csc-l0/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deepcharles%2Fcsc-l0/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29587077,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-18T16:55:40.614Z","status":"ssl_error","status_checked_at":"2026-02-18T16:55:37.558Z","response_time":162,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-10-15T02:15:25.625Z","updated_at":"2026-02-18T17:31:10.919Z","avatar_url":"https://github.com/deepcharles.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sparse convolutional sparse coding with $\\ell_0$ constraint\n\nTruong, C., \u0026 Moreau, T. (2024). Convolutional Sparse Coding for Time Series via a $\\ell_0$ Penalty: an Efficient Algorithm with Statistical Guarantees. _Statistical Analysis and Data Mining: The ASA Data Science Journal_.\n\n## Install\n\n```bash\npython -m pip install .\n```\n\n## Run on a step detection task\n\n```python\nfrom dicodile.data.gait import get_gait_data\nfrom alphacsc.init_dict import init_dictionary\nimport csc\nimport einops as ei\nimport matplotlib.pyplot as plt\n\n\ndef fig_ax(figsize=(20, 5)):\n    fig, ax = plt.subplots(figsize=figsize)\n    ax.set_xmargin(0)\n    return fig, ax\n\n\n# load data\ntrial = get_gait_data(subject=6, trial=1)\nsignal = trial[\"data\"][[\"RAX\", \"RAY\", \"RAZ\"]].pow(2).sum(axis=1).to_numpy().flatten()  # shape (n_samples,)\n\n\n# set dictionary size\nn_atoms = 5\n\n# set individual atom (patch) size.\nn_times_atom = 70\n\ndictionary = init_dictionary(\n    signal[None, None, :],\n    n_atoms=n_atoms,\n    n_times_atom=n_times_atom,\n    rank1=False,\n    window=True,\n    D_init=\"chunk\",\n    random_state=60,\n).squeeze()  # shape (n_atoms, n_times_atom)\n\n\n# Alternate between sparse coding and dictionary learning\nn_iter = 10\npenalty = 1e-3\nfor _ in range(n_iter):\n    activations = csc.update_z(\n        signal=signal,\n        dictionary=dictionary,\n        penalty=penalty,\n        constraint_str=csc.NO_CONSTRAINT,\n    )\n    dictionary = alphacsc.update_d.update_d(\n        X=signal[None, :],\n        Z=ei.rearrange(activations, \"n_times n_atoms -\u003e n_atoms 1 n_times\"),\n        n_times_atom=n_times_atom,\n    )[0]\n\n\n# Show activated time indexes\nfig, ax = fig_ax()\nax.plot(signal)\nactivations = csc.update_z(signal=signal, dictionary=dictionary, penalty=1)\nactivated_time_indexes = csc.get_temporal_support(activations)\nfor b in activated_time_indexes:\n    ax.axvline(b, color=\"k\", ls=\"--\")\n\n# Show reconstruction\nfrom alphacsc.utils import construct_X_multi\n\nZ_hat = ei.rearrange(activations, \"n_times n_atoms -\u003e 1 n_atoms n_times\")\nreconstruction = construct_X_multi(Z_hat, dictionary[:, None, :])[0][0]\n\nfig, ax = fig_ax()\nax.plot(signal[1_000:1_500])\nax.plot(reconstruction[1_000:1_500])\n\n```\n\n\n## Test the CDL\n\n```python\n\nimport numpy as np\nfrom dicodile.data.gait import get_gait_data\nfrom numpy.typing import NDArray\n\nimport csc\nimport alphacsc\nfrom alphacsc.update_d import _embed, solve_unit_norm_dual\n\n\ndef update_d(X, Z, n_times_atom, lambd0=None, ds_init=None, debug=False,\n             solver_kwargs=dict(), verbose=0):\n    \"\"\"Learn d's in time domain.\n\n    Parameters\n    ----------\n    X : array, shape (n_trials, n_times)\n        The data for sparse coding\n    Z : array, shape (n_trials, n_atoms, n_times - n_times_atom + 1)\n        The code for which to learn the atoms\n    n_times_atom : int\n        The shape of atoms.\n    lambd0 : array, shape (n_atoms,) | None\n        The init for lambda.\n    debug : bool\n        If True, check grad.\n    solver_kwargs : dict\n        Parameters for the solver\n    verbose : int\n        Verbosity level.\n\n    Returns\n    -------\n    d_hat : array, shape (k, n_times_atom)\n        The atom to learn from the data.\n    lambd_hats : float\n        The dual variables\n    \"\"\"\n    n_trials = len(Z)\n    n_atoms = Z[0].shape[1]\n\n    if lambd0 is None:\n        lambd0 = 10. * np.ones(n_atoms)\n\n    lhs = np.zeros((n_times_atom * n_atoms, ) * 2)\n    rhs = np.zeros(n_times_atom * n_atoms)\n    for i in range(n_trials):\n\n        ZZi = []\n        n_times_valid = Z[i].shape[0]\n        Zki = np.zeros(n_times_valid + 2*(n_times_atom - 1))\n        for k in range(n_atoms):\n            Zki[n_times_atom - 1:-(n_times_atom - 1)] = Z[i][:, k]\n            ZZik = _embed(Zki, n_times_atom)\n            # n_times_atom, n_times = ZZik.shape\n            ZZi.append(ZZik)\n\n        ZZi = np.concatenate(ZZi, axis=0)\n        lhs += np.dot(ZZi, ZZi.T)\n        rhs += np.dot(ZZi, X[i])\n\n    factr = solver_kwargs.get('factr', 1e7)  # default value\n    d_hat, lambd_hats = solve_unit_norm_dual(lhs, rhs, lambd0=lambd0,\n                                             factr=factr, debug=debug,\n                                             lhs_is_toeplitz=False)\n    d_hat = d_hat.reshape(n_atoms, n_times_atom)[:, ::-1]\n    return d_hat, lambd_hats\n\n\n# load data\ntrial = get_gait_data(subject=6, trial=1)\nsignal = trial[\"data\"][[\"RAX\", \"RAY\", \"RAZ\"]].pow(2).sum(axis=1).to_numpy().flatten()  # shape (n_samples,)\nX = [signal]\n\n# init dictionary\nn_atoms, n_times_atoms = 5, 70\ndictionary = np.random.randn(n_atoms, n_times_atoms)\ndictionary /= np.linalg.norm(dictionary, axis=1).reshape(-1, 1)\n\n# alternate between csc and cdl\nn_iter = 10\npenalty = 1\nlambd_hats = None\nfor k_iter in range(n_iter):\n    print(k_iter, end=\"\\t\")\n    print(\"csc\", end=\"...\")\n    activations = csc.update_z(\n        X=[signal],\n        dictionary=dictionary,\n        penalty=penalty,\n        constraint_str=csc.NO_CONSTRAINT,\n    )\n    print(\"ok\", end=\" \")\n    print(\"cdl\", end=\"...\")\n    dictionary, lambd_hats = update_d(\n        X=[signal],\n        Z=activations,\n        n_times_atom=n_times_atoms,\n        lambd0=lambd_hats\n    )\n    print(\"ok\")\n\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeepcharles%2Fcsc-l0","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeepcharles%2Fcsc-l0","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeepcharles%2Fcsc-l0/lists"}