Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PySpinW implementation #143

Merged
merged 40 commits into from
May 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
4edb62f
Initial test of pySpinW
wardsimon Mar 27, 2023
8188126
Initial test of pySpinW
wardsimon Mar 27, 2023
b2a83b6
Merge remote-tracking branch 'origin/pyspinw' into pyspinw
wardsimon Mar 27, 2023
b18f792
Merge remote-tracking branch 'origin/pyspinw' into pyspinw
wardsimon Mar 27, 2023
693146f
Merge remote-tracking branch 'origin/pyspinw' into pyspinw
wardsimon Mar 27, 2023
3c24178
Delete SpinW_2023a.ctf
wardsimon Apr 5, 2023
f757efd
Update versions
wardsimon Apr 5, 2023
0c27d4c
Merge remote-tracking branch 'SpinW/master' into pyspinw
wardsimon Apr 25, 2023
57cb6d9
Modify build
wardsimon Apr 25, 2023
734a8b0
Merge pull request #136 from wardsimon/pyspinw
wardsimon Apr 25, 2023
ac5b131
Update build_pyspinw.yml
wardsimon Apr 25, 2023
0dc53a0
Update build_ctf.m
wardsimon Apr 25, 2023
21913e3
Update build_pyspinw.yml
wardsimon Apr 25, 2023
6a712a2
Update build_pyspinw.yml
wardsimon Apr 25, 2023
ae522bd
Update build_pyspinw.yml
wardsimon Apr 25, 2023
cfe5538
Update build_pyspinw.yml
wardsimon Apr 25, 2023
9ae44b8
Adapt package init
wardsimon Apr 25, 2023
5215a0c
Update build_pyspinw.yml
wardsimon Apr 25, 2023
2aa5d45
Update build_pyspinw.yml
wardsimon Apr 25, 2023
a99fc87
Update test_spinw.py
wardsimon Apr 26, 2023
9ddf707
Fix license, init and an example
wardsimon Apr 26, 2023
f5e3c05
Update test_spinw.py
wardsimon Apr 27, 2023
dac9a09
Update build_pyspinw.yml
wardsimon Apr 28, 2023
bb3276f
Create release_notes.md
wardsimon Apr 28, 2023
cce5fe2
Update build_pyspinw.yml
wardsimon Apr 28, 2023
e6206f1
Try to build mex
wardsimon May 16, 2023
218fc23
Update build_pyspinw.yml
wardsimon May 16, 2023
d3ce642
Update build_pyspinw.yml
wardsimon May 16, 2023
4daa9ed
Update build_pyspinw.yml
wardsimon May 16, 2023
cd407f9
Modify remove and find
wardsimon May 16, 2023
c46278a
Update build_pyspinw.yml
wardsimon May 16, 2023
ca242b5
Update pyproject.toml
wardsimon May 16, 2023
174817c
Update build_pyspinw.yml
wardsimon May 16, 2023
adfbfde
Some pre-release optimisations
wardsimon May 16, 2023
19b8bb4
Update build_pyspinw.yml
wardsimon May 17, 2023
63050b8
Reverting back to manual removal of old mex files
wardsimon May 17, 2023
d9c72af
Update pyproject.toml
wardsimon May 17, 2023
b5d1cf1
Add version synchronisation
wardsimon May 17, 2023
5479cbf
Fix flaky test by specifying seed
wardsimon May 17, 2023
0c0de97
Update build_pyspinw.yml
wardsimon May 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion +sw_tests/+unit_tests/unittest_spinw_optmagk.m
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function test_wrong_shape_kbase_raises_error(testCase)
function test_fm_chain_optk(testCase)
testCase.swobj.addmatrix('label', 'J1', 'value', -1);
testCase.swobj.addcoupling('mat', 'J1', 'bond', 1);
out = testCase.swobj.optmagk;
out = testCase.swobj.optmagk('seed', 1);
out.stat = rmfield(out.stat, 'nFunEvals');

expected_mag_str = testCase.default_mag_str;
Expand Down
118 changes: 118 additions & 0 deletions .github/workflows/build_pyspinw.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
name: pySpinW

on: [push, workflow_dispatch]
mducle marked this conversation as resolved.
Show resolved Hide resolved

jobs:
compile_mex:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
matlab_version: [latest]
include:
- os: macos-latest
INSTALL_DEPS: brew install llvm libomp
fail-fast: true
runs-on: ${{ matrix.os }}
steps:
- name: Check out SpinW
uses: actions/checkout@v3
mducle marked this conversation as resolved.
Show resolved Hide resolved
with:
fetch-depth: 0
- name: Set up MATLAB
uses: matlab-actions/setup-matlab@v1 # v1.1.0 required for Windows/MacOS support
with:
release: ${{ matrix.matlab_version }}

- name: Remove old mex # This is due to find not working :-/ # find ${{ github.workspace }} -name "*.mex*" -type f -delete
mducle marked this conversation as resolved.
Show resolved Hide resolved
run: |
rm external/chol_omp/chol_omp.mexa64
rm external/chol_omp/chol_omp.mexmaci64
rm external/chol_omp/chol_omp.mexw64
rm external/eig_omp/eig_omp.mexa64
rm external/eig_omp/eig_omp.mexmaci64
rm external/eig_omp/eig_omp.mexw64
rm external/mtimesx/sw_mtimesx.mexa64
rm external/mtimesx/sw_mtimesx.mexmaci64
rm external/mtimesx/sw_mtimesx.mexw64
- name: Run MEXing
uses: matlab-actions/run-command@v1
with:
command: "addpath(genpath('swfiles')); addpath(genpath('external')); sw_mex('compile', true, 'test', false, 'swtest', false);"
- name: Upload MEX results
uses: actions/upload-artifact@v3
with:
name: MEX
path: ${{ github.workspace }}/external/**/*.mex*

build_ctfs:
needs: compile_mex
strategy:
matrix:
matlab_version: [R2021a, R2021b, R2022a, R2022b, R2023a]
runs-on: self-hosted
steps:
- name: Check out SpinW
uses: actions/checkout@v3
mducle marked this conversation as resolved.
Show resolved Hide resolved
with:
fetch-depth: 0
- name: Download MEX artifacts
uses: actions/download-artifact@v3
with:
name: MEX
path: ${{ github.workspace }}/external
- name: Build ctf
run: |
cd python
/Applications/MATLAB_${{ matrix.matlab_version }}.app/bin/matlab -nodisplay -r "build_ctf; exit"
- name: Upload CTF results
uses: actions/upload-artifact@v3
with:
name: CTF
path: ${{ github.workspace }}/python/ctf/*.ctf

build_wheel:
runs-on: ubuntu-latest
needs: build_ctfs
permissions:
contents: write
steps:
- name: Checkout SpinW
uses: actions/checkout@v3
- name: Download CTF artifacts
uses: actions/download-artifact@v3
with:
name: CTF
path: python/ctf
- name: Set up Python environment
uses: actions/setup-python@v4
with:
python-version: 3.8
- name: Move files
run: |
cd python
echo "PYSPINW_VERSION=$( cat pyproject.toml | grep "version = \"" | awk -F'"' '$0=$2' | sed 's/ //g' )" >> $GITHUB_ENV
mkdir pyspinw/ctfs
mv ctf/*.ctf pyspinw/ctfs
- name: Update Versions
if: startsWith(github.ref, 'refs/tags/v')
run: |
pip install poetry
cd ${{ github.workspace }}/python
poetry version $(git describe --tags --abbrev=0)
- name: Build Wheel
run: |
cd ${{ github.workspace }}/python
python -m pip wheel --no-deps --wheel-dir build .
- name: Create wheel artifact
uses: actions/upload-artifact@v3
with:
name: pySpinW Wheel
path: ${{ github.workspace }}/python/build/*.whl
- uses: ncipollo/release-action@v1
if: startsWith(github.ref, 'refs/tags/v')
with:
artifacts: ${{ github.workspace }}/python/build/*.whl
prerelease: true
replacesArtifacts: true
name: "pySpinW"
bodyFile: ${{ github.workspace }}/python/release_notes.md
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,10 @@ dev/standalone/MacOS/Source/
*.xml
*.rej

**profile_results
**profile_results
python/ctf
.idea/
**/*.pyc
python/ctf
.idea/
**/*.pyc
16 changes: 16 additions & 0 deletions python/build_ctf.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
out_dir = 'ctf';
VERSION = version('-release');
package_name = ['SpinW_', VERSION];
full_package = ['SpinW_', VERSION, '.ctf'];

opts = compiler.build.ProductionServerArchiveOptions( ...
['matlab', filesep, 'call.m'], ...
'ArchiveName', package_name, ...
'OutputDir', out_dir, ...
'AutoDetectDataFiles', 'on', ...
'AdditionalFiles', { ...
['..', filesep, 'swfiles'], ...
['..', filesep, 'external'], ...
['..', filesep, 'dat_files']});

compiler.build.productionServerArchive(opts);
164 changes: 164 additions & 0 deletions python/matlab/call.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
function [varargout] = call(name, varargin)
if strcmp(name, '_call_python')
varargout = call_python_m(varargin{:});
return
end
resultsize = nargout;
try
maxresultsize = nargout(name);
if maxresultsize == -1
maxresultsize = resultsize;
end
catch
maxresultsize = resultsize;
end
if resultsize > maxresultsize
resultsize = maxresultsize;
end
if nargin == 1
args = {};
else
args = varargin;
end
for ir = 1:numel(args)
args{ir} = unwrap(args{ir});
end
if resultsize > 0
% call the function with the given number of
% output arguments:
varargout = cell(resultsize, 1);
try
[varargout{:}] = feval(name, args{:});
catch err
if (strcmp(err.identifier,'MATLAB:unassignedOutputs'))
varargout = eval_ans(name, args);
else
rethrow(err);
end
end
else
varargout = eval_ans(name, args);
end
for ir = 1:numel(varargout)
varargout{ir} = wrap(varargout{ir});
end
end

function out = unwrap(in_obj)
out = in_obj;
if isstruct(in_obj) && isfield(in_obj, 'func_ptr') && isfield(in_obj, 'converter')
out = @(varargin) call('_call_python', [in_obj.func_ptr, in_obj.converter], varargin{:});
elseif isa(in_obj, 'containers.Map') && in_obj.isKey('wrapped_oldstyle_class')
out = in_obj('wrapped_oldstyle_class');
elseif iscell(in_obj)
for ii = 1:numel(in_obj)
out{ii} = unwrap(in_obj{ii});
end
end
end

function out = wrap(obj)
out = obj;
if isobject(obj) && (isempty(metaclass(obj)) && ~isjava(obj)) || has_thin_members(obj)
out = containers.Map({'wrapped_oldstyle_class'}, {obj});
elseif iscell(obj)
for ii = 1:numel(obj)
out{ii} = wrap(obj{ii});
end
end
end

function out = has_thin_members(obj)
% Checks whether any member of a class or struct is an old-style class
% or is already a wrapped instance of such a class
out = false;
if isobject(obj) || isstruct(obj)
try
fn = fieldnames(obj);
catch
return;
end
for ifn = 1:numel(fn)
try
mem = subsref(obj, struct('type', '.', 'subs', fn{ifn}));
catch
continue;
end
if (isempty(metaclass(mem)) && ~isjava(mem))
out = true;
break;
end
end
end
end

function results = eval_ans(name, args)
% try to get output from ans:
clear('ans');
feval(name, args{:});
try
results = {ans};
catch err
results = {[]};
end
end

function [n, undetermined] = getArgOut(name, parent)
undertermined = false;
if isstring(name)
fun = str2func(name);
try
n = nargout(fun);
catch % nargout fails if fun is a method:
try
n = nargout(name);
catch
n = 1;
undetermined = true;
end
end
else
n = 1;
undetermined = true;
end
end

function out = call_python_m(varargin)
% Convert row vectors to column vectors for better conversion to numpy
for ii = 1:numel(varargin)
if size(varargin{ii}, 1) == 1
varargin{ii} = varargin{ii}';
end
end
fun_name = varargin{1};
[kw_args, remaining_args] = get_kw_args(varargin(2:end));
if ~isempty(kw_args)
remaining_args = [remaining_args {struct('pyHorace_pyKwArgs', 1, kw_args{:})}];
end
out = call_python(fun_name, remaining_args{:});
if ~iscell(out)
out = {out};
end
end

function [kw_args, remaining_args] = get_kw_args(args)
% Finds the keyword arguments (string, val) pairs, assuming that they always at the end (last 2n items)
first_kwarg_id = numel(args) + 1;
for ii = (numel(args)-1):-2:1
if ischar(args{ii}); args{ii} = string(args{ii}(:)'); end
if isstring(args{ii}) && ...
strcmp(regexp(args{ii}, '^[A-Za-z_][A-Za-z0-9_]*', 'match'), args{ii})
% Python identifiers must start with a letter or _ and can contain charaters, numbers or _
first_kwarg_id = ii;
else
break;
end
end
if first_kwarg_id < numel(args)
kw_args = args(first_kwarg_id:end);
remaining_args = args(1:(first_kwarg_id-1));
else
kw_args = {};
remaining_args = args;
end
end
Loading